ভেরিয়েবল টাইপ হিসাবে জেনেরিক প্রোটোকল কীভাবে ব্যবহার করবেন


89

ধরা যাক আমার কাছে একটি প্রোটোকল রয়েছে:

public protocol Printable {
    typealias T
    func Print(val:T)
}

এবং এখানে বাস্তবায়ন হয়

class Printer<T> : Printable {

    func Print(val: T) {
        println(val)
    }
}

আমার প্রত্যাশাটি ছিল যে আমি অবশ্যই Printableএই জাতীয় মানগুলি মুদ্রণের জন্য পরিবর্তনশীলটি ব্যবহার করতে সক্ষম হব :

let p:Printable = Printer<Int>()
p.Print(67)

সংকলক এই ত্রুটির সাথে অভিযোগ করছে:

"প্রোটোকল 'মুদ্রণযোগ্য' কেবল জেনেরিক সীমাবদ্ধতা হিসাবে ব্যবহৃত হতে পারে কারণ এতে স্ব বা সম্পর্কিত প্রকারের প্রয়োজনীয়তা রয়েছে"

আমি কি ভুল কিছু করছি ? এটা সমাধান করার জন্য কোন রাস্তা আছে ?

**EDIT :** Adding similar code that works in C#

public interface IPrintable<T> 
{
    void Print(T val);
}

public class Printer<T> : IPrintable<T>
{
   public void Print(T val)
   {
      Console.WriteLine(val);
   }
}


//.... inside Main
.....
IPrintable<int> p = new Printer<int>();
p.Print(67)

সম্পাদনা 2: আমি যা চাই তার বাস্তব বিশ্বের উদাহরণ। নোট করুন যে এটি সংকলন করবে না, তবে আমি যা অর্জন করতে চাই তা উপস্থাপন করে।

protocol Printable 
{
   func Print()
}

protocol CollectionType<T where T:Printable> : SequenceType 
{
   .....
   /// here goes implementation
   ..... 
}

public class Collection<T where T:Printable> : CollectionType<T>
{
    ......
}

let col:CollectionType<Int> = SomeFunctiionThatReturnsIntCollection()
for item in col {
   item.Print()
}

4
অ্যাপল বিকাশকারী ফোরামে এখানে ২০১৪ সাল থেকে একটি প্রাসঙ্গিক থ্রেড দেওয়া আছে যেখানে অ্যাপলটির একজন সুইফট বিকাশকারী এই প্রশ্নটি (একটি ডিগ্রীতে) সম্বোধন করেছেন: devforums.apple.com/thread/230611 (দ্রষ্টব্য: একটি অ্যাপল বিকাশকারী অ্যাকাউন্ট এটি দেখার প্রয়োজন পৃষ্ঠা।)
টাইটানিয়ামডেকয়

উত্তর:


88

টমাস যেমন উল্লেখ করেছেন, আপনি কোনও প্রকার না দিয়েই আপনার পরিবর্তনশীল ঘোষণা করতে পারেন (বা আপনি স্পষ্টভাবে এটি টাইপ হিসাবে দিতে পারেন Printer<Int>But তবে আপনার কাছে Printableপ্রোটোকলটি কেন না থাকতে পারে তার একটি ব্যাখ্যা এখানে ।

আপনি নিয়মিত প্রোটোকলের মতো সম্পর্কিত ধরণের সাথে প্রোটোকলগুলি চিকিত্সা করতে পারবেন না এবং তাদেরকে স্ট্যান্ডেলোন ভেরিয়েবল প্রকার হিসাবে ঘোষণা করতে পারবেন না। কেন তা ভাবতে, এই দৃশ্যটি বিবেচনা করুন। মনে করুন আপনি কিছু স্বেচ্ছাচারী ধরণের সঞ্চয় এবং তারপরে এটি ফিরিয়ে আনার জন্য একটি প্রোটোকল ঘোষণা করেছেন:

// a general protocol that allows for storing and retrieving
// a specific type (as defined by a Stored typealias
protocol StoringType {
    typealias Stored

    init(_ value: Stored)
    func getStored() -> Stored
}

// An implementation that stores Ints
struct IntStorer: StoringType {
    typealias Stored = Int
    private let _stored: Int
    init(_ value: Int) { _stored = value }
    func getStored() -> Int { return _stored }
}

// An implementation that stores Strings
struct StringStorer: StoringType {
    typealias Stored = String
    private let _stored: String
    init(_ value: String) { _stored = value }
    func getStored() -> String { return _stored }
}

let intStorer = IntStorer(5)
intStorer.getStored() // returns 5

let stringStorer = StringStorer("five")
stringStorer.getStored() // returns "five"

ঠিক আছে, এখন পর্যন্ত খুব ভাল।

এখন, আপনার ভেরিয়েবলের প্রকারের প্রোটোকল হবার মূল কারণ হ'ল প্রকৃত প্রকারের চেয়ে প্রকারের প্রযোজনা, যাতে আপনি বিভিন্ন ধরণের অবজেক্টকে নির্ধারণ করতে পারেন যা সমস্ত সেই প্রোটোকলের সাথে একই ভেরিয়েবলের সাথে সামঞ্জস্য করে এবং পলিমারফিক পেতে পারে রানটাইমের সময় আচরণটি অবজেক্ট আসলে কী তার উপর নির্ভর করে।

প্রোটোকলের যদি কোনও সম্পর্কিত ধরণের থাকে তবে আপনি এটি করতে পারবেন না। নীচের কোডটি বাস্তবে কীভাবে কাজ করবে?

// as you've seen this won't compile because
// StoringType has an associated type.

// randomly assign either a string or int storer to someStorer:
var someStorer: StoringType = 
      arc4random()%2 == 0 ? intStorer : stringStorer

let x = someStorer.getStored()

উপরের কোডে, কী ধরণের xহবে? আন Int? বা ক String? সুইফটে, সমস্ত ধরণের অবশ্যই সংকলনের সময় স্থির করতে হবে। রানটাইমের সময় নির্ধারিত ফ্যাক্টরের উপর ভিত্তি করে একটি ফাংশন গতিশীলভাবে এক প্রকারের থেকে অন্য ধরণের কাছে ফিরে যেতে পারে না।

পরিবর্তে, আপনি কেবল StoredTypeজেনেরিক সীমাবদ্ধতা হিসাবে ব্যবহার করতে পারেন । মনে করুন আপনি কোনও ধরণের সঞ্চিত প্রিন্ট মুদ্রণ করতে চেয়েছিলেন। আপনি এই মত একটি ফাংশন লিখতে পারে:

func printStoredValue<S: StoringType>(storer: S) {
    let x = storer.getStored()
    println(x)
}

printStoredValue(intStorer)
printStoredValue(stringStorer)

এটি ঠিক আছে, কারণ সংকলনের সময়, এটি মনে হয় যেন সংকলক দুটি সংস্করণ লিখেছে printStoredValue: একটি Intএস এর জন্য , এবং একটি Stringএস এর জন্য । এই দুটি সংস্করণের মধ্যে, xএকটি নির্দিষ্ট ধরণের হিসাবে পরিচিত।


20
অন্য কথায় প্যারামিটার হিসাবে জেনেরিক প্রোটোকল থাকার কোনও উপায় নেই এবং কারণটি হ'ল সুইফট জেনারিকদের .NET স্টাইল রানটাইম সমর্থন করে না? এটি বেশ অসুবিধাজনক।
টেমরলেন

আমার .NET জ্ঞানটি কিছুটা দু: খজনক ... আপনার কাছে .NET- তে অনুরূপ কোনও উদাহরণ রয়েছে যা এই উদাহরণে কাজ করবে? এছাড়াও, আপনার উদাহরণের প্রোটোকল আপনাকে কী কিনছে তা দেখতে কিছুটা শক্ত। রানটাইমের সময়, আপনি যদি আপনার pভেরিয়েবলের জন্য বিভিন্ন ধরণের প্রিন্টার নিযুক্ত করেন এবং তারপরে অবৈধ প্রকারগুলিতে পাস করেন তবে আপনি কী আচরণটি আশা করবেন print? রানটাইম ব্যতিক্রম?
এয়ারস্পিডে গতিবেগ

@ আর্স্পিডভেলসিটি আমি সি # উদাহরণ অন্তর্ভুক্ত করতে প্রশ্ন আপডেট করেছি। পাশাপাশি আমার যে মূল্য প্রয়োজন তা হ'ল এটি আমাকে বাস্তবায়ন নয় বরং একটি ইন্টারফেসে উন্নীত করতে দেয়। যদি আমাকে কোনও ফাংশনে মুদ্রণযোগ্য পাস করতে হয় তবে আমি ঘোষণায় ইন্টারফেসটি ব্যবহার করতে পারি এবং আমার ফাংশনটি স্পর্শ না করে অনেক পার্থক্য বাস্তবায়ন পাস করতে পারি। সংগ্রহ লাইব্রেরি বাস্তবায়নের কথাও ভাবেন আপনার টাইপ টি তে এই জাতীয় কোড এবং অতিরিক্ত বাধা দরকার
টেমরলেন

4
তাত্ত্বিকভাবে, যদি সি # এর মতো কোণ বন্ধনী ব্যবহার করে জেনেরিক প্রোটোকল তৈরি করা সম্ভব হত, তবে কি প্রোটোকল ধরণের ভেরিয়েবল তৈরি করার অনুমতি দেওয়া হবে? (স্টোরিংটাইপ <<< স্টোরিং টাইপ <স্ট্রিং>)
GeRyCh

4
জাভাতে আপনি তার রূপরেখাটির সমতুল্য var someStorer: StoringType<Int>বা var someStorer: StoringType<String>সমাধান করতে পারেন।
জেরেমিপি

43

এই প্রশ্নে আরও একটি সমাধান পাওয়া যায় নি যা টাইপ ইরেজোর নামে একটি কৌশল ব্যবহার করছে । জেনেরিক প্রোটোকলের জন্য একটি বিমূর্ত ইন্টারফেস অর্জন করতে, এমন একটি শ্রেণি বা কাঠামো তৈরি করুন যা কোনও বস্তু বা কাঠামোকে জড়িয়ে দেয় যা প্রোটোকলের সাথে সঙ্গতিপূর্ণ হয়। সাধারণত 'যেকোন {প্রোটোকল নাম named' নামে পরিচিত মোড়ক শ্রেণি নিজেই প্রোটোকলের সাথে সম্মতি দেয় এবং অভ্যন্তরীণ বস্তুতে সমস্ত কল ফরোয়ার্ড করে তার ফাংশনগুলি প্রয়োগ করে। খেলার মাঠে নীচের উদাহরণটি ব্যবহার করে দেখুন:

import Foundation

public protocol Printer {
    typealias T
    func print(val:T)
}

struct AnyPrinter<U>: Printer {

    typealias T = U

    private let _print: U -> ()

    init<Base: Printer where Base.T == U>(base : Base) {
        _print = base.print
    }

    func print(val: T) {
        _print(val)
    }
}

struct NSLogger<U>: Printer {

    typealias T = U

    func print(val: T) {
        NSLog("\(val)")
    }
}

let nsLogger = NSLogger<Int>()

let printer = AnyPrinter(base: nsLogger)

printer.print(5) // prints 5

ধরণের হিসাবে printerপরিচিত হয়AnyPrinter<Int> প্রোটোকলের যেকোন সম্ভাব্য বাস্তবায়ন বিমূর্ত করতে ব্যবহৃত হতে এবং । যদিও অ্যানিপ্রিন্টার প্রযুক্তিগতভাবে বিমূর্ত নয়, এটি বাস্তবায়ন কেবল বাস্তব বাস্তবায়নের ধরণের মধ্যে পড়ে এবং এগুলি প্রয়োগ করে প্রয়োগকারী প্রকারগুলি ডিকুয়াল করতে ব্যবহৃত হতে পারে।

একটি বিষয় লক্ষণীয় তা হ'ল AnyPrinterস্পষ্টভাবে বেস উদাহরণটি ধরে রাখতে হবে না। আসলে, আমরা পারি না যেহেতু আমরা AnyPrinterকোনও Printer<T>সম্পত্তি থাকার ঘোষণা করতে পারি না । পরিবর্তে, আমরা _printবেস এর printফাংশন একটি ফাংশন পয়েন্টার পেতে । কল base.printনা করে কল করা এমন একটি ফাংশন ফিরিয়ে দেয় যেখানে বেসটি স্ব ভেরিয়েবল হিসাবে সজ্জিত হয় এবং ভবিষ্যতে অনুরোধগুলির জন্য এইভাবে ধরে রাখা হয়।

আরেকটি বিষয় মনে রাখা উচিত হ'ল এই সমাধানটি মূলত গতিশীল প্রেরণের আরেকটি স্তর যার অর্থ পারফরম্যান্সে সামান্য আঘাত। এছাড়াও, মোছার ধরণটি অন্তর্নিহিত উদাহরণের উপরে অতিরিক্ত মেমরির প্রয়োজন। এই কারণে, প্রকার মুছে ফেলা ব্যয় মুক্ত বিমূর্ততা নয়।

স্পষ্টতই টাইপ ইরেজ সেটআপ করার জন্য কিছু কাজ রয়েছে, তবে জেনেরিক প্রোটোকল বিমূর্তকরণ প্রয়োজন হলে এটি খুব কার্যকর হতে পারে। এই ধরণটি স্যুইফ্ট স্ট্যান্ডার্ড লাইব্রেরিতে এই জাতীয় প্রকারের সাথে পাওয়া যায় AnySequence। আরও পড়ুন: http://robnapier.net/erasure

বোনাস:

আপনি যদি স্থির করেন যে আপনি Printerসর্বত্র একই প্রয়োগ বাস্তবায়ন করতে চান , তবে আপনি একটি সুবিধামত প্রারম্ভিক সরবরাহ করতে পারেন AnyPrinterযার জন্য এই ধরণের ইনজেকশন।

extension AnyPrinter {

    convenience init() {

        let nsLogger = NSLogger<T>()

        self.init(base: nsLogger)
    }
}

let printer = AnyPrinter<Int>()

printer.print(10) //prints 10 with NSLog

আপনি আপনার অ্যাপ্লিকেশন জুড়ে ব্যবহার করেন এমন প্রোটোকলের জন্য নির্ভরতা ইনজেকশন প্রকাশ করার জন্য এটি একটি সহজ এবং DRY উপায় হতে পারে।


এর জন্য ধন্যবাদ. আমি মুছে ফেলার এই ধরণটি পছন্দ করি (ফাংশন পয়েন্টার ব্যবহার করে) একটি বিমূর্ত শ্রেণি ব্যবহার করার চেয়ে ভাল (যা অবশ্যই বিদ্যমান নেই এবং এটি ব্যবহার করে নকল হওয়া আবশ্যক fatalError()) যা অন্য ধরণের ক্ষয়ের টিউটোরিয়ালে বর্ণিত হয়েছে।
চেজ

4

আপনার আপডেট ব্যবহারের ক্ষেত্রে সম্বোধন:

(বিটিডাব্লু Printableইতিমধ্যে একটি স্ট্যান্ডার্ড সুইফ্ট প্রোটোকল যাতে বিভ্রান্তি এড়াতে আপনি সম্ভবত একটি আলাদা নাম বেছে নিতে চান)

প্রোটোকল প্রয়োগকারীদের উপর নির্দিষ্ট বিধিনিষেধ প্রয়োগ করার জন্য, আপনি প্রোটোকলের টাইপালিয়ায় বাধা দিতে পারেন। সুতরাং আপনার প্রোটোকল সংগ্রহ তৈরি করতে যাতে উপাদানগুলির মুদ্রণযোগ্য হতে হবে:

// because of how how collections are structured in the Swift std lib,
// you’d first need to create a PrintableGeneratorType, which would be
// a constrained version of GeneratorType
protocol PrintableGeneratorType: GeneratorType {
    // require elements to be printable:
    typealias Element: Printable
}

// then have the collection require a printable generator
protocol PrintableCollectionType: CollectionType {
    typealias Generator: PrintableGenerator
}

এখন আপনি যদি কেবল এমন মুদ্রণযোগ্য উপাদান থাকতে পারে এমন কোনও সংগ্রহ বাস্তবায়ন করতে চান:

struct MyPrintableCollection<T: Printable>: PrintableCollectionType {
    typealias Generator = IndexingGenerator<T>
    // etc...
}

তবে এটি সম্ভবত সামান্য প্রকৃত উপযোগিতা হিসাবে আপনি যেহেতু বিদ্যমান সুইফ্ট সংগ্রহের স্ট্রাক্টগুলিকে সীমাবদ্ধ করতে পারবেন না, কেবলমাত্র আপনি প্রয়োগ করেন।

পরিবর্তে, আপনার জেনেরিক ফাংশন তৈরি করা উচিত যা মুদ্রণযোগ্য উপাদানগুলি সহ সংগ্রহগুলিতে তাদের ইনপুটকে সীমাবদ্ধ করে।

func printCollection
    <C: CollectionType where C.Generator.Element: Printable>
    (source: C) {
        for x in source {
            x.print()
        }
}

ওহ মানুষ এটা অসুস্থ দেখাচ্ছে। আমার যা প্রয়োজন তা হ'ল জেনেরিক সমর্থন সহ প্রোটোকল have আমি এমন কিছু আশা করছিলাম: প্রোটোকল সংগ্রহ <টি>: সিকোয়েন্সটাইপ। এবং এটাই. কোড নমুনাগুলির জন্য ধন্যবাদ আমি মনে করি এটি হজম হতে কিছুটা সময় লাগবে :)
টেমরলেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.