'রেফ' এবং 'আউট' পলিমারফিজমকে সমর্থন করে না কেন?


124

নিম্নলিখিত নিন:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

উপরোক্ত সংকলন-সময় ত্রুটি কেন ঘটে? এটি উভয় refএবং outযুক্তি দিয়ে ঘটে ।

উত্তর:


169

=============

আপডেট: আমি এই উত্তরটি এই ব্লগ প্রবেশের ভিত্তি হিসাবে ব্যবহার করেছি:

কেন রেফ এবং আউট প্যারামিটারগুলি প্রকারের প্রকরণের অনুমতি দেয় না?

এই বিষয়ে আরও মন্তব্য করার জন্য ব্লগ পৃষ্ঠা দেখুন। মহান প্রশ্নের জন্য ধন্যবাদ।

=============

এর আপনি ক্লাস আছে অনুমান করা যাক Animal, Mammal, Reptile, Giraffe, Turtleএবং Tigerসুস্পষ্ট subclassing সম্পর্ক রয়েছে।

এখন ধরুন আপনার একটা পদ্ধতি আছে void M(ref Mammal m)Mউভয়ই পড়তে এবং লিখতে পারে m


আপনি কি টাইপের একটি ভেরিয়েবল পাস করতেAnimal পারেন M?

না। এই ভেরিয়েবলটিতে একটি থাকতে পারে Turtleতবে Mধরে নেবে এটিতে কেবল স্তন্যপায়ী প্রাণি রয়েছে। ক Turtleনা ক Mammal

উপসংহার 1 : refপরামিতিগুলি "বড়" করা যায় না। (স্তন্যপায়ী প্রাণীর চেয়ে আরও বেশি প্রাণী রয়েছে, তাই ভেরিয়েবলটি "বড়" হয়ে চলেছে কারণ এতে আরও বেশি জিনিস থাকতে পারে))


আপনি কি টাইপের একটি ভেরিয়েবল পাস করতেGiraffe পারেন M?

না। Mলিখতে পারেনm , এবং Mএকটি লিখতে চান পারে Tigerমধ্যে m। এখন আপনি Tigerএকটি ভেরিয়েবলের মধ্যে একটি রেখেছেন যা আসলে টাইপের Giraffe

উপসংহার 2 : refপ্যারামিটারগুলি "ছোট" করা যায় না।


এখন বিবেচনা করুন N(out Mammal n)

আপনি কি টাইপের একটি ভেরিয়েবল পাস করতে পারেন? Giraffe পারেন N?

না। Nলিখতে পারেনn , এবং Nএকটি লিখতে চাইতে পারে Tiger

উপসংহার 3 :out প্যারামিটারগুলি "ছোট" করা যায় না।


আপনি কি টাইপের একটি ভেরিয়েবল পাস করতে পারেন? Animal পারেন N?

হুম।

ভাল, কেন না? Nথেকে পড়তে পারে না n, এটি কেবল এটি লিখতে পারে, তাই না? আপনি একটি লিখুনTiger টাইপের বৈকল্পের জন্য একটিAnimal এবং আপনি ঠিক আছে, ঠিক আছে?

ভুল। নিয়মটি " Nকেবল লিখতে পারে না"n " ।

নিয়মগুলি হ'ল সংক্ষেপে:

1) সাধারণত ফিরে আসার আগে Nলিখতে হয়। (যদিnNN ছোঁড়া হয় তবে সমস্ত বেট বন্ধ রয়েছে)

2) Nএটি থেকে কিছু nপড়ার আগে কিছু লিখতে হবেn

এটি ইভেন্টের এই ক্রমকে অনুমতি দেয়:

  • xপ্রকারের ক্ষেত্রটি ঘোষণা করুনAnimal
  • xএকটি outপ্যারামিটার হিসাবে পাসN
  • Nএকটি Tigerমধ্যে লিখুন n, যা একটি উপনামx
  • অন্য থ্রেডে, কেউ একটি লিখেছেন Turtleমধ্যেx
  • Nএর বিষয়বস্তু পড়ার চেষ্টা করে nএবং আবিষ্কার Turtleকরে যা এটি ভেরিয়েবলের ধরণের বলে মনে করে Mammal

স্পষ্টতই আমরা এটিকে অবৈধ করতে চাই।

উপসংহার 4 : outপ্যারামিটারগুলি "বৃহত্তর" করা যায় না।


চূড়ান্ত উপসংহার : উভয়ই refবা outপ্যারামিটারগুলি তাদের ধরণের পরিবর্তিত হতে পারে। অন্যথায় এটি যাচাইযোগ্যযোগ্য সুরক্ষা ভঙ্গ করা।

যদি মৌলিক ধরণের তত্ত্বটি এই বিষয়গুলিতে আগ্রহী, তবে C # 4.0 এ কীভাবে সমবায়তা এবং বৈপরীত্য কাজ করে তা সম্পর্কে আমার সিরিজটি পড়ার বিষয়টি বিবেচনা করুন


6
+1 টি। বাস্তব-বিশ্বমানের উদাহরণগুলি ব্যবহার করে দুর্দান্ত ব্যাখ্যা যা ইস্যুগুলি স্পষ্টভাবে প্রদর্শন করে (যেমন - এ, বি এবং সি দিয়ে ব্যাখ্যা করে কেন এটি কাজ করে না তা প্রদর্শন করা আরও শক্ত করে তোলে)।
গ্রান্ট ওয়াগনার

4
আমি এই চিন্তাধারার প্রক্রিয়াটি পড়ে নিচু মনে করি। আমি মনে করি আমি আরও ভাল বই ফিরে পেতে!
স্কট ম্যাককেঞ্জি

এই ক্ষেত্রে, আমরা সত্যিই অ্যাবস্ট্রাক্ট ক্লাস ভেরিয়েবলটি আর্গুমেন্ট হিসাবে ব্যবহার করতে পারি না এবং এটি থেকে উত্পন্ন শ্রেণীর অবজেক্টটি পাস করতে পারি !!
প্রশান্ত চোলাচাগুদ্দা

তবুও, কেন outপ্যারামিটারগুলি "বৃহত্তর" করা যায় না? আপনার বর্ণিত ক্রমটি কেবলমাত্র outপ্যারামিটার ভেরিয়েবল নয়, কোনও ভেরিয়েবলের ক্ষেত্রে প্রয়োগ করা যেতে পারে । এবং পাঠককে Mammalসে হিসাবে এটি অ্যাক্সেস করার চেষ্টা করার আগে যুক্তির মানটি নিক্ষেপ করা দরকার Mammalএবং অবশ্যই তিনি বিবেচনা না করলে ব্যর্থ হতে পারে
astef

29

কারণ উভয় ক্ষেত্রে আপনাকে অবশ্যই প্যারামিটার / রেফ / আউটকে মান নির্ধারণ করতে সক্ষম হতে হবে।

আপনি যদি রেফারেন্স হিসাবে b কে Foo2 পদ্ধতিতে পাস করার চেষ্টা করেন এবং Foo2 এ আপনি একটি = নতুন এ () প্রদান করার চেষ্টা করেন তবে এটি অবৈধ।
একই কারণে আপনি লিখতে পারবেন না:

B b = new A();

+1 সোজা বিন্দু এবং কারণ পুরোপুরি ভাল ব্যাখ্যা।
রুই ক্রেভেরিও

10

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

OPতিহ্যগতভাবে স্ট্যাটিকভাবে টাইপসেইফ থাকা অবস্থায় ওওপি ভাষাগুলি যেগুলি প্রদর্শিত হয় সেগুলি হ'ল "প্রতারণা" (লুকানো গতিশীল টাইপের চেক সন্নিবেশ করানো, বা চেক করার জন্য সমস্ত উত্সের সংকলন-সময় পরীক্ষা প্রয়োজন); মৌলিক পছন্দটি হ'ল: হয় এই ovক্যবদ্ধতা ত্যাগ করুন এবং অনুশীলনকারীদের ধাঁধাটি গ্রহণ করুন (যেমন সি # এখানে রয়েছে), অথবা একটি গতিশীল টাইপিং পদ্ধতির দিকে চলে যান (প্রথম OOP ভাষা হিসাবে, ছোট্ট, করেছে), বা অবিচ্ছিন্ন (একক- কার্যনির্বাহী ভাষার মতো অ্যাসাইনমেন্ট) ডেটা (অপরিবর্তনশীলতার অধীনে, আপনি কোভারিয়েন্সকে সমর্থন করতে পারেন, এবং অন্যান্য সম্পর্কিত ধাঁধা যেমন এড়ানো যায় যে কোনও পরিবর্তনীয়-ডেটা বিশ্বে স্কোয়ার সাবক্লাস আয়তক্ষেত্র থাকতে পারে না) এড়াতে পারেন।


4

বিবেচনা:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

এটি প্রকারের সুরক্ষা লঙ্ঘন করবে


সমস্যাটি হ'ল ভারের কারণে এটি "অস্পষ্ট" ধরণের অস্পষ্ট more

আমার ধারণা 6 লাইনে আপনি => বি বি = নাল;
আলেজান্দ্রো মিরালস

@ অ্যামিরালস - হ্যাঁ, এটি varসম্পূর্ণ ভুল ছিল। সংশোধন করা হয়েছে।
হেন্ক হলটারম্যান

4

অন্য প্রতিক্রিয়াগুলি এই আচরণের পেছনের যুক্তিটি সংক্ষিপ্তভাবে ব্যাখ্যা করেছে, তবে আমি মনে করি এটি উল্লেখ করার মতো যে আপনাকে যদি সত্যই এই প্রকৃতির কিছু করার দরকার হয় তবে আপনি Foo2 কে জেনেরিক পদ্ধতিতে তৈরি করে অনুরূপ কার্যকারিতা অর্জন করতে পারেন, যেমন:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

2

কারণ Foo2একটি দেওয়ার ref Bফলে একটি ত্রুটিযুক্ত বস্তুর ফলাফল ঘটবে কারণ Foo2কেবলমাত্র কীভাবে তার Aঅংশ পূরণ করতে হয় তা জানে B


0

যে সংকলকটি আপনাকে বলছে এটি কি আপনি স্পষ্টভাবে বস্তুটি নিক্ষেপ করতে চান যাতে এটি নিশ্চিত হওয়া যায় যে আপনার উদ্দেশ্যগুলি কী?

Foo2(ref (A)b)

এটি করা যায় না, "একটি রেফ বা আউট আর্গুমেন্ট অবশ্যই একটি নির্ধারিত পরিবর্তনশীল হতে হবে"

0

সুরক্ষা দৃষ্টিকোণ থেকে বোঝা যায়, তবে কম্পাইলার যদি ত্রুটির পরিবর্তে একটি সতর্কতা দেয় তবে আমি এটিকে পছন্দ করতাম, কারণ রেফারেন্সের মাধ্যমে বহুভুজ সংক্রান্ত বস্তুর বৈধ ব্যবহার রয়েছে। যেমন

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

এটি সংকলন করবে না, তবে এটি কি কাজ করবে?


0

আপনি যদি আপনার ধরণের জন্য ব্যবহারিক উদাহরণ ব্যবহার করেন তবে আপনি এটি দেখতে পাবেন:

SqlConnection connection = new SqlConnection();
Foo(ref connection);

এবং এখন আপনি আপনার ফাংশন যা পূর্বপুরুষ গ্রহণ করে ( ie Object ):

void Foo2(ref Object connection) { }

এতে কী ভুল হতে পারে?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

আপনি শুধুমাত্র একটি দায়িত্ব অর্পণ করা পরিচালিত Bitmapআপনার টু SqlConnection

এটা কোন ভাল।


অন্যদের সাথে আবার চেষ্টা করুন:

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

আপনি OracleConnectionআপনার একটি ওভার-টপ স্টাফ করেছেন SqlConnection


0

আমার ক্ষেত্রে আমার ফাংশন কোনও বস্তু গ্রহণ করেছে এবং আমি কোনও কিছু প্রেরণ করতে পারি নি তাই আমি সহজ করেছিলাম

object bla = myVar;
Foo(ref bla);

এবং যে কাজ করে

আমার ফু ভিবি.এনইটিতে রয়েছে এবং এটি ভিতরে প্রকারের জন্য পরীক্ষা করে এবং অনেক যুক্তি দেয়

আমার উত্তর সদৃশ থাকলে আমি ক্ষমা চাইছি তবে অন্যরা খুব দীর্ঘ ছিল

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.