সি # ও জেনারিক্স - কেন নতুন ক্লাসের পরিবর্তে ডাইভারিভেড ক্লাসে বেস ক্লাস বলা হয়?

যদি জেনেরিক টাইপ আর্গুমেন্ট (কোনও কলিং ক্লাস বা কলিং পদ্ধতির) যেখানে T: বেস টিতে নতুন পদ্ধতিতে প্রাপ্ত করা হয় তা বলা হয় না, পরিবর্তে বেসে পদ্ধতিটি বলা হয়।

পদ্ধতি টির জন্য টাইপ টি কি উপেক্ষা করা হয় যদিও রান সময় আগে এটি জানা উচিত?

Update: BUT, when the constraint is using an interface like where T : IBase the method in Base class is called (not the method in interface, which is also impossible).
So that means the system actually is able to detect the types that far and go beyond the type constraint! Then why doesn't it go beyond the type constraint in case of class-typed constraint?
Does that mean that the method in Base class that implements the interface has implicit override keyword for the method?

পরীক্ষা কোড:

public interface IBase
{
    void Method();
}

public class Base : IBase 
{
    public void Method()
    {

    }
}

public class Derived : Base
{
    public int i = 0;

    public new void Method()
    {
        i++;
    }
}

public class Generic
    where T : Base
{
    public void CallMethod(T obj)
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod2(T2 obj)
        where T2 : T
    {
        obj.Method();  //calls Base.Method()
    }
}

public class GenericWithInterfaceConstraint
    where T : IBase
{
    public void CallMethod(T obj)
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod2(T2 obj)
        where T2 : T
    {
        obj.Method();  //calls Base.Method()
    }
}

public class NonGeneric
{
    public void CallMethod(Derived obj)
    {
        obj.Method();  //calls Derived.Method()
    }

    public void CallMethod2(T obj)
        where T : Base
    {
        obj.Method();  //calls Base.Method()
    }

    public void CallMethod3(T obj)
        where T : IBase
    {
        obj.Method();  //calls Base.Method()
    }
}

public class NewMethod
{
    unsafe static void Main(string[] args)
    {
        Generic genericObj = new Generic();
        GenericWithInterfaceConstraint genericObj2 = new GenericWithInterfaceConstraint();
        NonGeneric nonGenericObj = new NonGeneric();
        Derived obj = new Derived();

        genericObj.CallMethod(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj2.CallMethod(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        genericObj2.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod(obj);  //calls Derived.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod2(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        nonGenericObj.CallMethod3(obj);  //calls Base.Method()
        Console.WriteLine(obj.i);

        obj.Method();  //calls Derived.Method()
        Console.WriteLine(obj.i);
    }
}

আউটপুট:

0
0
0
0
1
1
1
2
0
আপনি কি মনে করেন ইন্টারফেস-সীমাবদ্ধ এক IBase.Method কল করা হয় না? ObjIBase এবং এটিতে পদ্ধতি কাস্ট করার চেষ্টা করুন।
যোগ লেখক supercat, উৎস
উল্লেখ্য, পরিবর্তে যদি আপনি পরিবর্তে লিখতে পারেন তবে এটি একটি পার্থক্য তৈরি করে: বীজ, আইবিস </কোড>, যেমন ইন্টারফেসটি ডিক্লেয়ার্ড ক্লাসের ঘোষণায় পুনরাবৃত্তি করুন। এটি ইন্টারফেস পুনরায় প্রয়োগকরণ নামে পরিচিত। বৈশিষ্টটি দেখুন
যোগ লেখক Jeppe Stig Nielsen, উৎস

4 উত্তর

গতিশীল </কোড> বস্তুর ব্যবহার ছাড়া, সি # সবসময় কম্পাইলের সময় পদ্ধতিগুলি বাঁধে - জেনেরিকের ব্যবহার করার সময়ও। ভার্চুয়াল পদ্ধতির কলগুলি বাস্তবায়িত পদ্ধতির পরিবর্তে ভার্চুয়াল পদ্ধতির স্লটগুলিতে আবদ্ধ থাকে, যাতে যখন তারা ডেরিভেটিভ-শ্রেণী অবজেক্টগুলিতে সঞ্চালিত হয় তখন তারা ডাইরেক্ট-ক্লাসের বাস্তবায়নে পরিচালিত হবে; যদিও যে পদ্ধতিগুলি স্লট বিন্দু চালানোর সময় নির্ধারিত হবে, স্লটগুলিতে বাঁধাই কম্পাইলের সময় ঘটে। যদি একটি ডেরিভেটিভ-ক্লাস পদ্ধতি ঘোষিত হয় তবে নতুন পরিবর্তে ওভাররাইড , কোড যা বহির্মুখী শ্রেণির দ্বারা আবদ্ধ থাকবে সেটি উপজাত শ্রেণী পদ্ধতি ব্যবহার করবে, কিন্তু কোডটি ব্যবহার করে আবদ্ধ থাকবে। বেস ক্লাস বেস ক্লাস পদ্ধতি ব্যবহার করবে।

এই কেস হতে হবে কেন বুঝতে, এটি না হলে কল্পনা। যদি কোড <�বেস> একটি পদ্ধতি ঘোষিত হয় তবে কি হওয়া উচিত int Foo() , এবং একটি শ্রেণী <�কোড> গঠিত: বেস একটি নতুন স্ট্রিং Foo ঘোষণা করে ) </কোড>। যদি একটি জেনেরিক ক্লাস সংকোচন T: বেস কোডের Foo কোডের একটি বস্তুর উপর T কল করার চেষ্টা করে, তাহলে ঐ পদ্ধতির রিটার্ন টাইপ কি হওয়া উচিত? থাকা?

0
যোগ
রিটার্ন টাইপ সঙ্গে ভাল বিন্দু (আপনি পদ্ধতি বাঁধাই এবং ভার্চুয়াল কল এবং স্লট সম্পর্কে যেতে পারেন, কিন্তু আমাদের কিছু একটি সুস্পষ্ট ব্রেকিং উদাহরণ এটি চিন্তা করার প্রয়োজন।)
যোগ লেখক Rawling, উৎস
@ রাউলিং: বাইন্ডিংয়ের কথা বলার কারণ হচ্ছে, কম্পাইলারের দৃষ্টিকোণ থেকে, একটি জেনেরিক টাইপ T Foo থেকে সীমাবদ্ধ) অনেক উপায়ে একটি Foo প্রকৃত টাইপের মত যা রান কোডে T এর জন্য প্রতিস্থাপিত হতে পারে। জেনারিক্স সি। ++++++++++++++++++++++++++++++++
যোগ লেখক supercat, উৎস
@ রোল্যান্ড পিহ্লাকস: আমি সন্দেহ করছি যে, একমাত্র কারণ, প্রকৃতপক্ষে। এমনকি যদি কেউ এমন নিয়ম প্রণয়ন করতে পারে যা সংকলককে এমন সব সমস্যাগুলির সমাধান করতে সক্ষম করে, যেমন জেনারিকের আচরণগুলি যেমন রান-টাইম বজায় রাখে, তবুও প্রত্যেক জেনারিক পদ্ধতিতে সেগুলি ব্যবহৃত জেনেরিক প্রকারের প্রতিটি সংমিশ্রণে পুনরায় কম্পাইল করা প্রয়োজন। । যে তুলনামূলকভাবে সামান্য সুবিধা প্রদানের সময় এটি গুরুতর কর্মক্ষমতা ক্ষতিগ্রস্ত হবে
যোগ লেখক supercat, উৎস
@ রোল্যান্ড পিহ্লাকস: জেনেরিক ব্যবহার করে রান-টাইমের পরিবর্তে কম্পাইলের সময় বিভিন্ন ধরণের ধরনের চেক সঞ্চালন করা সম্ভব। উদাহরণস্বরূপ, কোড যদি IEnumerable থেকে আইটেমটি পড়েন তবে এটি আইটেমটি একটি Animal হিসাবে রান-টাইমে চেক না করেই ব্যবহার করতে পারে কিনা তা নাকি না । এই উভয়ই কোড চালানোর সময় টাইপের টাইম চেক করতে হবে (যা সময় নিতে হবে) এবং একটি রান টাইম টাইপ চেক ব্যর্থ হতে পারে সম্ভাবনা সরিয়ে দেয়।
যোগ লেখক supercat, উৎস
@ রোল্যান্ড পিহ্লাকস: এটিও মনে রাখা গুরুত্বপূর্ণ যে কম্পাইলারের C ++ প্রোগ্রামের জন্য, কম্পাইলারটি সব ধরনের templated প্রকারগুলি সনাক্ত করতে সক্ষম হবে যা প্রোগ্রামের কার্যনির্বাহী সময় তৈরি হতে পারে এবং কম্পাইল করা প্রোগ্রামের আকার সমানুপাতিক হবে বিভিন্ন প্রকারের সংখ্যার সংখ্যার যা একটি পদ্ধতি ব্যবহার করতে পারে। রিফ্লেকশন ছাড়াও, কোনও একটি প্রোগ্রাম লিখতে পারে যা একটি অবাধ-দৈর্ঘ্যের স্ট্রিং ডিজিট (যেমন 24601 ) নিতে পারে এবং একটি নেস্টেড জেনেরিক টাইপ (যেমন <�কোড> X2 <�এক্সএক্স >>>>> এ)। নেস্টেড জেনেরিক প্রকারের সংখ্যা থেকে ...
যোগ লেখক supercat, উৎস
... যেমন একটি রুটিন জেনেরিক রুটিন অতিক্রম করতে পারে মহাবিশ্বের পারমাণবিক সংখ্যা সংখ্যা বহির্ভূত হবে, কোন উপায় হবে কম্পাইলার তাদের জন্য কোড প্রাক উত্পন্ন করতে পারে। JITTER এর উৎপন্ন যে প্রতিটি ধরনের জন্য একবার জেনেরিক পদ্ধতি কম্পাইল করা সম্ভব হবে, কিন্তু পারফরম্যান্স খরচ সম্ভবত পর্যন্ত উপকারের অতিক্রম করবে।
যোগ লেখক supercat, উৎস
@ রোল্যান্ড পিহ্লাকস: মাইক্রোসফট জেটি কম্পাইলারকে এভাবে ডিজাইন করতে পারে, কিন্তু এটি একটি রান-টাইম পারফরম্যান্স খরচ উল্লেখ করে থাকতে পারে এবং সেখানে এমন অনেকগুলি পরিস্থিতি নেই যেখানে এটি এমন কোনও কিছু সম্পন্ন করবে যা একটি সমন্বয় ব্যবহার করে ভাল কাজ করা যাবে না। কম্পাইল-সময় ভার্চুয়াল ফাংশন এবং প্রতিফলন এর বাঁধাই এর। এমনকি যদিও .net এটি ব্যবহার করে বর্গ প্রকারের প্রতিটি সংমিশ্রণে জেনেরিক কোডকে পুনরায় কম্পাইল না করে, জেনেরিক ক্লাসগুলি জেনেরিক টাইপ প্যারামিটারের প্রতিটি সংমিশ্রনের জন্য স্ট্যাটিক ভেরিয়েবলের একটি পৃথক সেট ধারণ করে।
যোগ লেখক supercat, উৎস
@ সুপারক্যাট্যাট: ধন্যবাদ, আমি অনুমান করি যদি রিটার্ন ধরনের নতুন পদ্ধতির সমস্যা না হয় তাহলে নকশাটি ভিন্ন হতে পারে। এই নকশা সিদ্ধান্তের কারণটি আমাকে একক বলে মনে হচ্ছে বাঁধাই এবং ইত্যাদি অপ্রচলিত না সম্পর্কে সবকিছুই শুধু ফলাফল অনুমান করা হয়। আমি ওভারলোডিং এর অস্তিত্ব সম্পর্কে জানি যে প্রশ্নে আরও স্পষ্টভাবে বিবৃত করা উচিত।
যোগ লেখক Roland Pihlakas, উৎস
@ সুপারক্যাট্যাট: হ্যাঁ, এটি অনিবার্য হবে যে, তারপর বিভিন্ন জেনেরিক প্রকারের জন্য একাধিক পদ্ধতি সংকলন করা হচ্ছে। কিন্তু সি ++ কি তাও না, তাই না? আমি আশা করছিলাম জেনারিক ব্যবহার করে আমাকে ভার্চুয়াল কল পরিত্রাণ পেতে সাহায্য করবে। এই মত মনে হয় না এবং জেনেরিক একমাত্র পারফরম্যান্স-সম্পর্কিত ব্যবহার হয় বক্সিং মান প্রকারগুলি এড়াতে। ক্লাসের জন্য, ইন্টারফেস (বা ভার্চুয়াল পদ্ধতির সাথে বেস ক্লাস) পরিবর্তে জেনারিক আর্গুমেন্ট ব্যবহার করে কর্মক্ষমতা বেনিফিটগুলি অফার করতে পারে না।
যোগ লেখক Roland Pihlakas, উৎস
@ সুপারক্যাট্যাট: হ্যাঁ, আমি আশা করছিলাম যে জেনেটিক পদ্ধতি/প্রকার জেনেটিক জেনারেটরগুলি জেনারিকভাবে তৈরি করা হয় কারণ তারা প্রথমেই ইস্যু করা হয়। একইভাবে এক সময় স্ট্যাটিক টাইপ প্রারম্ভিক
যোগ লেখক Roland Pihlakas, উৎস

এটি কারণ টি </কোড> বেস এর শব্দার্থ থাকতে পারে। আমি রানটাইম এ টাইপ বাঁধাই সঙ্গে যাচ্ছে কি ঠিক আপনি বলতে পারবেন না, কিন্তু এই আমার শিক্ষিত অনুমান হয়।

আপনি পদ্ধতিটি সঠিকভাবে ওভাররাইড করছেন না, বরং "নতুন" এর মাধ্যমে গোপন করছেন, যদি আপনি বেস ক্লাসের একটি রেফারেন্স ব্যবহার করেন তবে আপনি কোনও গোপন বাইপাস করছেন। এই যেখানে লুকানো পড়ে নিচে

যে সদস্যরা অন্য সদস্যদের লুকিয়ে রাখে তারা কেবলমাত্র যদি আপনি এমন একটি রেফারেন্স ব্যবহার করেন যা তারা লুকায়িত থাকে আপনি সবসময় বেস ক্লাসে একটি রেফারেন্স ব্যবহার করে একটি লুকানো সদস্য বাইপাস করতে পারেন:

var derived = new Derived();
var baseRef = (Base)derived;
baseRef.Method();//calls Base.Method instead of Derived.Method.

একটি পদ্ধতি সঠিকভাবে ওভাররাইড করার জন্য এবং এই কোডটি কাজ করার জন্য, বেস কোডে ভার্চুয়াল পদ্ধতি হিসাবে চিহ্নিত করুন এবং ওভাররাইড এটিকে প্রাপ্ত শ্রেণীতে চিহ্নিত করুন।

class Base
{
    public virtual void Method() {}
}

class Derived : Base
{
    public override void Method() {}
}

আপনি এটি প্রমাণ করতে পারেন, আপনার জেনারিক সীমাবদ্ধতাটি পরিবর্তন করতে যেখানে T: প্রাপ্ত করা এবং এটি "নতুন" সদস্যকে আঘাত করা উচিত।

0
যোগ

যে অপারেটর নতুন প্রকৃতির কারণে: নতুন বিপরীত ওভাররাইড, বেস নামে একই নামের একটি ফাংশন তৈরি করুন, যা বেস পদ্ধতিটি মাস্ক করে কিন্তু এটি অগ্রাহ্য করে না।

একটি সঠিক কাস্ট ছাড়াই, মূল কারণটি উল্লেখ করা হবে যদি রেফারেন্স টাইপ বেস এর হয়।

0
যোগ

নতুন কীওয়ার্ডটি কেবল ওভারলোডিং এর পরিবর্তে পদ্ধতি লুকায়। কারণ আপনার অ জেনারিক CallMethod প্রত্যাশিত হিসাবে কাজ প্রদর্শিত হয় কারণ পদ্ধতি স্বাক্ষর একটি প্রাপ্ত বেস এর পরিবর্তে আশা করে।

জেনেরিক সত্যিই এখানে অপরাধী হয় না। যদি আপনি পদ্ধতিতে মেথড স্বাক্ষর পরিবর্তন করেন (বেস obj) , আপনি জেনেরিক বাস্তবায়ন হিসাবে একই "অপ্রত্যাশিত" আচরণ দেখতে পাবেন এবং নিম্নলিখিত আউটপুট পাবেন:

0
0
0
0
0
0
0
1

যদি আপনি Base.Method ভার্চুয়াল তৈরি করেন এবং এটি Derived.method এর সাথে ওভাররাইড করেন:

public class Base 
{
    public virtual void Method()
    {

    }
}

public class Derived : Base
{
    public int i = 0;

    public override void Method()
    {
        i++;
    }
}

আপনি নিম্নলিখিত আউটপুট পাবেন:

1
2
3
4
5
6
7
8

Edit: updated to match question's updated output.

0
যোগ