উত্তর:
প্রশ্নটি হল "স্ববিরোধ ও বৈপরীত্যের মধ্যে পার্থক্য কী?"
কোভারিয়েন্স এবং কন্ট্রভিয়ারিয়েন্স হ'ল ম্যাপিং ফাংশনের বৈশিষ্ট্য যা কোনও সেটের এক সদস্যকে অন্যজনের সাথে যুক্ত করে । আরও সুনির্দিষ্টভাবে, ম্যাপিংটি সেই সেটটির কোনও সম্পর্কের ক্ষেত্রে সমবায় বা বিপরীতমুখী হতে পারে ।
সমস্ত সি # প্রকারের সেটের নিম্নলিখিত দুটি উপসেট বিবেচনা করুন। প্রথম:
{ Animal,
Tiger,
Fruit,
Banana }.
এবং দ্বিতীয়, এটি পরিষ্কারভাবে সম্পর্কিত সেট:
{ IEnumerable<Animal>,
IEnumerable<Tiger>,
IEnumerable<Fruit>,
IEnumerable<Banana> }
একটা হল ম্যাপিং দ্বিতীয় সেটে প্রথম সেট থেকে অপারেশন। অর্থাৎ প্রথম সেটে প্রতিটি টিয়ের জন্য, দ্বিতীয় সেটে সংশ্লিষ্ট টাইপটি হয় IEnumerable<T>
। বা, সংক্ষিপ্ত আকারে, ম্যাপিং হয় T → IE<T>
। লক্ষ্য করুন এটি একটি "পাতলা তীর"।
এতদিন আমার সাথে?
এখন একটি সম্পর্ক বিবেচনা করা যাক । প্রথম সেটে জোড়া ধরণের মধ্যে একটি কার্যক্রমে সামঞ্জস্যতার সম্পর্ক রয়েছে। প্রকারের Tiger
একটি ভেরিয়েবলের জন্য টাইপের একটি মান নির্ধারিত হতে পারে Animal
, সুতরাং এই ধরণেরগুলি "অ্যাসাইনমেন্ট সামঞ্জস্যপূর্ণ" হিসাবে বলা হয়। আসুন লিখুন "ধরনের একটি মান X
ধরনের একটি পরিবর্তনশীল নির্ধারিত করা যেতে পারে Y
একটি সংক্ষিপ্ত আকারে": X ⇒ Y
। লক্ষ্য করুন এটি একটি "চর্বিযুক্ত তীর"।
সুতরাং আমাদের প্রথম সাবসেটে, এখানে সমস্ত অ্যাসাইনমেন্টের সামঞ্জস্যতা সম্পর্ক রয়েছে:
Tiger ⇒ Tiger
Tiger ⇒ Animal
Animal ⇒ Animal
Banana ⇒ Banana
Banana ⇒ Fruit
Fruit ⇒ Fruit
সি # 4-এ, যা নির্দিষ্ট ইন্টারফেসের কোভেরিয়েন্ট অ্যাসাইনমেন্টের সামঞ্জস্যকে সমর্থন করে, দ্বিতীয় সেটে জোড়া ধরণের মধ্যে একটি অ্যাসাইনমেন্টের সামঞ্জস্যতা সম্পর্ক রয়েছে:
IE<Tiger> ⇒ IE<Tiger>
IE<Tiger> ⇒ IE<Animal>
IE<Animal> ⇒ IE<Animal>
IE<Banana> ⇒ IE<Banana>
IE<Banana> ⇒ IE<Fruit>
IE<Fruit> ⇒ IE<Fruit>
লক্ষ্য করুন যে ম্যাপিং T → IE<T>
অ্যাসাইনমেন্টের সামঞ্জস্যের অস্তিত্ব এবং দিকটি সংরক্ষণ করে । এটি, যদি X ⇒ Y
, তবে এটিও সত্য IE<X> ⇒ IE<Y>
।
যদি আমাদের চর্বিযুক্ত তীরের দুপাশে দুটি জিনিস থাকে, তবে আমরা উভয় পক্ষকে একটি অনুরূপ সরু তীরের ডানদিকে কিছু দিয়ে প্রতিস্থাপন করতে পারি।
কোনও নির্দিষ্ট সম্পর্কের ক্ষেত্রে এই সম্পত্তি থাকা ম্যাপিংকে "কোভারিয়েন্ট ম্যাপিং" বলা হয়। এটি বোঝা উচিত: টাইগারদের একটি ক্রম ব্যবহার করা যেতে পারে যেখানে প্রাণীর একটি অনুক্রম প্রয়োজন, তবে বিপরীতটি সত্য নয়। যেখানে বাঘের ক্রম প্রয়োজন সেখানে প্রাণীর একটি ক্রম প্রয়োজনীয়ভাবে ব্যবহার করা যায় না।
এটাই সমবায়। এখন সমস্ত ধরণের সেটটির এই উপসেটটি বিবেচনা করুন:
{ IComparable<Tiger>,
IComparable<Animal>,
IComparable<Fruit>,
IComparable<Banana> }
এখন আমাদের প্রথম সেট থেকে তৃতীয় সেট পর্যন্ত ম্যাপিং রয়েছে T → IC<T>
।
সি # 4 এ:
IC<Tiger> ⇒ IC<Tiger>
IC<Animal> ⇒ IC<Tiger> Backwards!
IC<Animal> ⇒ IC<Animal>
IC<Banana> ⇒ IC<Banana>
IC<Fruit> ⇒ IC<Banana> Backwards!
IC<Fruit> ⇒ IC<Fruit>
অর্থাৎ ম্যাপিং T → IC<T>
করেছে অস্তিত্ব সংরক্ষিত কিন্তু দিক বিপরীত নিয়োগ সামঞ্জস্য করুন। মানে, যদি X ⇒ Y
তারপর, IC<X> ⇐ IC<Y>
।
একটি ম্যাপিং যা কোনও সম্পর্ক সংরক্ষণ করে তবে এর বিপরীত হয় তাকে কনক্র্যাভারেন্ট ম্যাপিং বলে।
আবার এটি পরিষ্কারভাবে সঠিক হওয়া উচিত। একটি ডিভাইস যা দুটি প্রাণীর তুলনা করতে পারে তা দুটি বাঘের সাথে তুলনা করতে পারে, তবে একটি ডিভাইস যা দুটি বাঘের তুলনা করতে পারে তা প্রয়োজনীয়ভাবে কোনও দুটি প্রাণীর তুলনা করতে পারে না।
সুতরাং যে সি সহভেদাংক এবং contravariance পার্থক্য # 4. সহভেদাংক অপরিবর্তিত assignability দিক। বৈপরীত্য এটি বিপরীত ।
IEnumerable<Tiger>
থেকে IEnumerable<Animal>
নিরাপদে? কারণ জিরাফটিকে ইনপুট করার কোনও উপায় নেই IEnumerable<Animal>
। কেন আমরা একটি রূপান্তর করতে পারেন IComparable<Animal>
করতে IComparable<Tiger>
? কারণ জি থেকে জিরাফ বের করার কোনও উপায় নেই IComparable<Animal>
। ধারণা তৈরী কর?
উদাহরণ দেওয়া সম্ভবত সবচেয়ে সহজ - আমি অবশ্যই তাদের স্মরণ করব।
সহভেদাংক
ক্যানোনিকাল উদাহরণ: IEnumerable<out T>
,Func<out T>
আপনি এর থেকে রূপান্তর করতে পারেন IEnumerable<string>
থেকে IEnumerable<object>
, অথবা Func<string>
করার Func<object>
। মানগুলি কেবল এই বিষয়গুলি থেকে প্রকাশিত হয় ।
এটি কাজ করে কারণ আপনি যদি কেবলমাত্র API এর বাইরে মানগুলি নিয়ে চলেছেন এবং এটি নির্দিষ্ট (যেমন string
) কিছু ফেরত চলেছে , আপনি সেই প্রত্যাশিত মানটিকে আরও সাধারণ ধরণের হিসাবে পছন্দ করতে পারেন (যেমন object
)।
Contravariance
ক্যানোনিকাল উদাহরণ: IComparer<in T>
,Action<in T>
আপনি এর থেকে রূপান্তর করতে পারেন IComparer<object>
থেকে IComparer<string>
, অথবা Action<object>
করার Action<string>
; মান শুধুমাত্র যেতে মধ্যে এই বস্তু।
এবার এটি কাজ করে কারণ এপিআই যদি সাধারণ কিছু (যেমন object
) আশা করে তবে আপনি এটিকে আরও নির্দিষ্ট কিছু (যেমন string
) দিতে পারেন ।
আরো সাধারণভাবে
আপনি একটি ইন্টারফেস থাকে, তাহলে IFoo<T>
তাতে covariant হতে পারে T
(যেমন কি তা মেনে নেবে যেমন IFoo<out T>
যদি T
শুধুমাত্র (যেমন একটি রিটার্ন টাইপ) ইন্টারফেস মধ্যে একটি আউটপুট অবস্থানে ব্যবহার করা হয়। এটা contravariant হতে পারে T
(যেমন IFoo<in T>
) যদি T
শুধুমাত্র একটি ইনপুট অবস্থানে ব্যবহার করা হয় ( যেমন একটি পরামিতি প্রকার)।
এটি সম্ভাব্য বিভ্রান্তিকর হয়ে পড়ে কারণ "আউটপুট পজিশন" যেমনটি শোনা যায় তত সহজ নয় - টাইপের একটি প্যারামিটার Action<T>
এখনও কেবল T
একটি আউটপুট পজিশনে ব্যবহার করে - Action<T>
যদি আপনি বুঝতে চান আমার অর্থ কি round এটি একটি "আউটপুট" যাতে মানগুলি পদ্ধতিটির প্রয়োগ থেকে কলারের কোডের দিকে যেতে পারে ঠিক যেমন কোনও ফেরতের মান পারে can সাধারণত এই ধরণের জিনিসটি আসে না, ভাগ্যক্রমে :)
Action<T>
এখনও কেবলমাত্র T
আউটপুট অবস্থানে ব্যবহার করে " । Action<T>
রিটার্ন টাইপ অকার্যকর, কিভাবে এটি T
আউটপুট হিসাবে ব্যবহার করতে পারে ? বা এর অর্থ কি এটি, কারণ এটি এমন কোনও কিছু ফেরত দেয় না যে আপনি দেখতে পাচ্ছেন যে এটি কখনই নিয়ম লঙ্ঘন করতে পারে না?
আমি আশা করি যে আমার পোস্টটি বিষয়টির একটি ভাষা-অজ্ঞাতদৃষ্টি দেখতে সহায়তা করবে।
আমাদের অভ্যন্তরীণ প্রশিক্ষণের জন্য আমি "স্মার্টটাক, অবজেক্টস এবং ডিজাইন (চামন্ড লিউ)" দুর্দান্ত বইয়ের সাথে কাজ করেছি এবং আমি নিম্নলিখিত উদাহরণগুলি পুনরায় লিখেছি।
"ধারাবাহিকতা" অর্থ কী? ধারণাটি হ'ল উচ্চ-বদলযোগ্য প্রকারের সাথে টাইপ-নিরাপদ প্রকারের স্তরক্রম ডিজাইন করা। এই ধারাবাহিকতাটি পাওয়ার কীটি হ'ল সাব টাইপ ভিত্তিক কনফারেন্স, যদি আপনি স্ট্যাটিকালি টাইপ করা ভাষায় কাজ করেন। (আমরা এখানে একটি উচ্চ স্তরের লিসকভ সাবস্টিটিউশন নীতি (এলএসপি) নিয়ে আলোচনা করব।)
ব্যবহারিক উদাহরণ (সিউডে সিউডো কোড / অবৈধ):
কোভেরিয়েন্স: আসুন স্ট্রিটিক টাইপিংয়ের সাথে ডিমগুলিকে "ধারাবাহিকভাবে" রাখার পাখি ধরে নেওয়া যাক: পাখিটি যদি ডিম দেয় তবে বার্ডের সাব টাইপটি ডিমের একটি টাইপটি রাখে না? উদাহরণস্বরূপ হাঁসের ধরণটি একটি DuckEgg দেয়, তারপরে ধারাবাহিকতা দেওয়া হয়। কেন এই ধারাবাহিকতা? কারণ এই জাতীয় অভিব্যক্তিতে: Egg anEgg = aBird.Lay();
এবার্ড উল্লেখটি পাখির দ্বারা বা হাঁসের উদাহরণ দ্বারা আইনত প্রতিস্থাপন করা যেতে পারে। আমরা বলি যে রিটার্নের ধরণটি প্রকারের সমান্তরাল, যেখানে স্তর () সংজ্ঞায়িত হয়। একটি সাব টাইপের ওভাররাইড আরও বিশিষ্ট টাইপ ফিরে আসতে পারে। => "তারা আরও সরবরাহ করে।"
বৈপরীত্য: আসুন পিয়ানোস ধরে নিই যে পিয়ানোবাদীরা স্ট্যাটিক টাইপিংয়ের সাথে "ধারাবাহিকভাবে" খেলতে পারে: পিয়ানোবাদক যদি পিয়ানো বাজান, তবে তিনি কি গ্র্যান্ডপিয়ানো বাজতে পারবেন? বরং কোনও ভার্চুসো গ্র্যান্ডপিয়ানো খেলবে না? (সতর্ক হোন; একটি মোড় আছে!) এটি বেমানান! কারণ এই জাতীয় অভিব্যক্তিতে: aPiano.Play(aPianist);
পিয়ানো আইনজীবিভাবে পিয়ানো বা গ্র্যান্ডপিয়ানো উদাহরণ দিয়ে এপিওয়ানকে প্রতিস্থাপন করতে পারে না! একটি গ্র্যান্ডপিয়ানো কেবল ভার্চুওসো দ্বারা বাজানো যায়, পিয়ানোবাদীরা খুব সাধারণ! গ্র্যান্ডপিয়ানস অবশ্যই আরও সাধারণ ধরণের দ্বারা প্লেযোগ্য হবে, তারপরে নাটকটি সামঞ্জস্যপূর্ণ। আমরা বলি যে পরামিতি টাইপটি প্রকারের তুলনায় বিপরীত, যাতে প্লে () সংজ্ঞায়িত হয়। একটি সাব টাইপের ওভাররাইড আরও সাধারণ আকারের গ্রহণ করতে পারে। => "তাদের কম প্রয়োজন” "
সি # তে ফিরে আসুন:
সি # মূলত একটি স্ট্যাটিক্যালি টাইপড ল্যাঙ্গুয়েজ হওয়ায় একটি ধরণের ইন্টারফেসের "অবস্থানগুলি" যা কো-বা বিপরীত (যেমন প্যারামিটার এবং রিটার্নের ধরণের) হওয়া উচিত, সেই ধরণের ধারাবাহিক ব্যবহার / বিকাশের গ্যারান্টি হিসাবে স্পষ্টভাবে চিহ্নিত করতে হবে , LSP কাজ সূক্ষ্ম করতে। গতিশীলভাবে টাইপ করা ভাষায় এলএসপি ধারাবাহিকতা সাধারণত কোনও সমস্যা হয় না, অন্য কথায় আপনি নেট-ইন্টারফেস এবং প্রতিনিধিদের উপর সহ-এবং বিপরীত "মার্কআপ" থেকে সম্পূর্ণরূপে মুক্তি পেতে পারেন, যদি আপনি কেবল আপনার প্রকারে ডায়নামিক টাইপ ব্যবহার করেন। - তবে এটি সি # এর সেরা সমাধান নয় (আপনার সর্বজনীন ইন্টারফেসে ডায়নামিক ব্যবহার করা উচিত নয়)।
তত্ত্বের দিকে ফিরে যান:
বর্ণিত কনফারেন্স (কোভারিয়েন্ট রিটার্ন টাইপ / কনভারট্রিয়েন্ট প্যারামিটার প্রকার) হ'ল তাত্ত্বিক আদর্শ (ভাষা পান্না এবং পোল -১ দ্বারা সমর্থিত)। কিছু ওপ ভাষা (যেমন আইফেল) অন্য ধরণের ধারাবাহিকতা প্রয়োগ করার সিদ্ধান্ত নিয়েছে, esp। কোভেরিয়েন্ট প্যারামিটারের ধরণগুলি কারণ এটি তাত্ত্বিক আদর্শের চেয়ে বাস্তবতাকে আরও ভালভাবে বর্ণনা করে। স্ট্যাটিকালি টাইপ করা ভাষাগুলিতে প্রায়শই "ডাবল প্রেরণ" এবং "দর্শনার্থী" এর মতো নকশার ধরণ প্রয়োগ করে কাঙ্ক্ষিত ধারাবাহিকতা অর্জন করতে হবে। অন্যান্য ভাষাগুলি তথাকথিত "একাধিক প্রেরণ" বা একাধিক পদ্ধতি সরবরাহ করে (এটি মূলত রান সময় ফাংশন ওভারলোডগুলি নির্বাচন করা হয় , যেমন সিএলওএস সহ) বা গতিশীল টাইপিং ব্যবহার করে পছন্দসই প্রভাব পান।
Bird
সংজ্ঞায়িত করা হয় public abstract BirdEgg Lay();
তবে তা প্রয়োগ করা Duck : Bird
আবশ্যকpublic override BirdEgg Lay(){}
সুতরাং আপনার দৃ ser ়বিজ্ঞানের যে BirdEgg anEgg = aBird.Lay();
কোনও প্রকারের বৈকল্পিকতা রয়েছে তা কেবল অসত্য। ব্যাখ্যার মূল বিষয় হওয়ায় পুরো পয়েন্টটি এখন চলে গেছে। আপনি কি পরিবর্তে বলতে পারবেন যে বাস্তবায়নের মধ্যে যেখানে ডাকইগি স্পষ্টভাবে বার্ডএগজি আউট / রিটার্নের ধরণে প্রেরণ করা হয়েছে সেখানে সমবায়তা বিদ্যমান? যেভাবেই হোক, দয়া করে আমার বিভ্রান্তি দূর করুন।
DuckEgg Lay()
কোনও বৈধ ওভাররাইড নয় এবং এটি ক্রুজ। সি # কোভেরিয়েন্ট রিটার্নের ধরণগুলিকে সমর্থন করে না, তবে জাভা পাশাপাশি সি ++ করে। আমি বরং সি #-এর মতো সিনট্যাক্স ব্যবহার করে তাত্ত্বিক আদর্শটি বর্ণনা করেছি। সি # তে আপনাকে বার্ড এবং হাঁসকে একটি সাধারণ ইন্টারফেস প্রয়োগ করতে দেওয়া হবে, যেখানে লে-কে কোভারিয়েন্ট রিটার্ন (যেমন আউট-স্পেসিফিকেশন) টাইপ হিসাবে সংজ্ঞায়িত করা হয়েছে, তারপরে বিষয়গুলি একসাথে ফিট হতে পারে! Egg Lay()
extends
, গ্রাহক super
"।
রূপান্তরকারী প্রতিনিধি আমাকে পার্থক্য বুঝতে সাহায্য করে।
delegate TOutput Converter<in TInput, out TOutput>(TInput input);
TOutput
প্রতিনিধিত্ব করে সহভেদাংক যেখানে একটি পদ্ধতি একটি ফেরৎ আরো নির্দিষ্ট ধরনের ।
TInput
বিপরীতে প্রতিনিধিত্ব করে যেখানে কোনও পদ্ধতি কম নির্দিষ্ট প্রকারে পাস করা হয় ।
public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }
public static Poodle ConvertDogToPoodle(Dog dog)
{
return new Poodle() { Name = dog.Name };
}
List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();
কো এবং কনট্রা ভেরিয়েন্স বেশ যুক্তিসঙ্গত জিনিস। ভাষা টাইপ সিস্টেম আমাদের বাস্তব জীবনের যুক্তি সমর্থন করতে বাধ্য করে। উদাহরণ দিয়ে বোঝা সহজ।
উদাহরণস্বরূপ আপনি একটি ফুল কিনতে চান এবং আপনার শহরে আপনার দুটি ফুলের দোকান রয়েছে: গোলাপের দোকান এবং ডেইজি শপ।
আপনি যদি কাউকে জিজ্ঞাসা করেন "ফুলের দোকানটি কোথায়?" এবং কেউ আপনাকে বলবে গোলাপের দোকান কোথায়, ঠিক আছে? হ্যাঁ, কারণ গোলাপ একটি ফুল, আপনি যদি ফুল কিনতে চান তবে আপনি একটি গোলাপ কিনতে পারেন। যদি কেউ আপনাকে ডেইজি শপের ঠিকানা দিয়ে উত্তর দেয় তবে এটি একই প্রযোজ্য।
এই উদাহরণ হল সহভেদাংক : আপনি ঢালাই করার অনুমতি দেওয়া হয় A<C>
থেকে A<B>
, যেখানে C
একটি উপশ্রেণী হয় B
, যদি A
(ফাংশন থেকে ফলে আয়) জেনেরিক মান উৎপন্ন হয়। out
কোভারিয়েন্স নির্মাতাদের সম্পর্কে, এজন্য সি # কোভেরিয়েন্সের জন্য কীওয়ার্ড ব্যবহার করুন।
প্রকার:
class Flower { }
class Rose: Flower { }
class Daisy: Flower { }
interface FlowerShop<out T> where T: Flower {
T getFlower();
}
class RoseShop: FlowerShop<Rose> {
public Rose getFlower() {
return new Rose();
}
}
class DaisyShop: FlowerShop<Daisy> {
public Daisy getFlower() {
return new Daisy();
}
}
প্রশ্ন "ফুলের দোকানটি কোথায়?", উত্তরটি "গোলাপের দোকান সেখানে":
static FlowerShop<Flower> tellMeShopAddress() {
return new RoseShop();
}
উদাহরণস্বরূপ আপনি আপনার গার্লফ্রেন্ডকে একটি ফুল উপহার দিতে চান এবং আপনার গার্লফ্রেন্ড কোনও ফুল পছন্দ করে। আপনি কি তাকে এমন একজন ব্যক্তি হিসাবে বিবেচনা করতে পারেন যা গোলাপকে ভালোবাসে, বা ডাইজি পছন্দ করে এমন ব্যক্তি হিসাবে? হ্যাঁ, কারণ তিনি যদি কোনও ফুল পছন্দ করেন তবে তিনি গোলাপ এবং ডেইজি উভয়কেই পছন্দ করবেন।
এই একটি উদাহরণ contravariance : আপনি ঢালাই করার অনুমতি দেওয়া করছি A<B>
করার A<C>
, যেখানে C
এর উপশ্রেণী হয় B
যদি, A
হ্রাস জেনেরিক মান। কনট্রাভারিয়েন্স গ্রাহকরা সম্পর্কে, এজন্য সি # বিপরীতার জন্য কীওয়ার্ডটি in
ব্যবহার করে।
প্রকার:
interface PrettyGirl<in TFavoriteFlower> where TFavoriteFlower: Flower {
void takeGift(TFavoriteFlower flower);
}
class AnyFlowerLover: PrettyGirl<Flower> {
public void takeGift(Flower flower) {
Console.WriteLine("I like all flowers!");
}
}
আপনি আপনার গার্লফ্রেন্ডকে বিবেচনা করছেন যিনি যে কোনও ফুলকে গোলাপ ভালবাসেন এবং তাকে একটি গোলাপ উপহার হিসাবে ভালবাসেন:
PrettyGirl<Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());