সি # জেনারিকস - কীভাবে অপ্রয়োজনীয় পদ্ধতি এড়ানো যায়?


28

আসুন ধরে নেওয়া যাক আমার দুটি ক্লাস রয়েছে যা দেখতে এরকম দেখাচ্ছে (কোডের প্রথম ব্লক এবং সাধারণ সমস্যা C # এর সাথে সম্পর্কিত):

class A 
{
    public int IntProperty { get; set; }
}

class B 
{
    public int IntProperty { get; set; }
}

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

আমি IntPropertyউভয় শ্রেণীর সম্পত্তি নিয়ে কিছু যুক্তি প্রয়োগ করতে চাই এবং সি ++ এ আমি এটি খুব সহজেই করতে একটি টেম্পলেট শ্রেণি ব্যবহার করতে পারি:

template <class T>
class LogicToBeApplied
{
    public:
        void T CreateElement();

};

template <class T>
T LogicToBeApplied<T>::CreateElement()
{
    T retVal;
    retVal.IntProperty = 50;
    return retVal;
}

এবং তারপরে আমি এরকম কিছু করতে পারি:

LogicToBeApplied<ClassA> classALogic;
LogicToBeApplied<ClassB> classBLogic;
ClassA classAElement = classALogic.CreateElement();
ClassB classBElement = classBLogic.CreateElement();   

এইভাবে আমি একটি একক জেনেরিক কারখানার শ্রেণি তৈরি করতে পারি যা ক্লাসএ এবং ক্লাসবি উভয়ের পক্ষে কাজ করে।

যাইহোক, সি # তে, whereযুক্তির কোডটি হুবহু হলেও আমাকে দুটি পৃথক ধারা সহ দুটি ক্লাস লিখতে হবে :

public class LogicAToBeApplied<T> where T : ClassA, new()
{
    public T CreateElement()
    {
        T retVal = new T();
        retVal.IntProperty = 50;
        return retVal;
    }
}

public class LogicBToBeApplied<T> where T : ClassB, new()
{
    public T CreateElement()
    {
        T retVal = new T();
        retVal.IntProperty = 50;
        return retVal;
    }
}

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

কেউ কি এমন কোনও পদ্ধতির পরামর্শ দিতে পারেন যেখানে এটি আরও মার্জিত ফ্যাশনে লেখা যেতে পারে?


3
আপনি কেন প্রথমে এর জন্য জেনেরিক ব্যবহার করছেন? এই দুটি ফাংশন সম্পর্কে জেনেরিক কিছুই নেই।
লুয়ান

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

6
ঠিক আছে, আপনি যদি প্রতিশ্রুতি বা গতিশীলতা ব্যবহার করতে পারেন তবে যদি আপনি আত্মবিশ্বাসী হন যে তারা ভবিষ্যতে প্রকাশে এটি ভাঙবে না।
কেসি

জেনারিকদের সম্পর্কে এটি আমার সবচেয়ে বড় অভিযোগ হ'ল এটি এটি করতে পারে না।
জোশুয়া

1
@ জোশুয়া, আমি এটিকে "হাঁসের টাইপিং" সমর্থন করে না এমন ইন্টারফেসের একটি সমস্যা হিসাবে আরও মনে করি।
ইয়ান

উত্তর:


49

প্রক্সি ইন্টারফেস যুক্ত করুন (কখনও কখনও সূক্ষ্ম পার্থক্যের সাথে মাঝে মাঝে অ্যাডাপ্টার বলা হয় ), LogicToBeAppliedপ্রক্সিটির ক্ষেত্রে প্রয়োগ করুন, তারপরে দুটি ল্যাম্বডাস থেকে এই প্রক্সিটির উদাহরণ তৈরির উপায় যুক্ত করুন: সম্পত্তি পাওয়ার জন্য একটি এবং সেটটির জন্য একটি one

interface IProxy
{
    int Property { get; set; }
}
class LambdaProxy : IProxy
{
    private Function<int> getFunction;
    private Action<int> setFunction;
    int Property
    {
        get { return getFunction(); }
        set { setFunction(value); }
    }
    public LambdaProxy(Function<int> getter, Action<int> setter)
    {
        getFunction = getter;
        setFunction = setter;
    }
}

এখন, যখনই আপনাকে একটি আইপ্রোকিতে পাস করতে হবে তবে তৃতীয় পক্ষের ক্লাসগুলির উদাহরণ রয়েছে, আপনি কিছু ল্যাম্বডাসে পাস করতে পারবেন:

A a = new A();
B b = new B();
IProxy proxyA = new LambdaProxy(() => a.Property, (val) => a.Property = val);
IProxy proxyB = new LambdaProxy(() => b.Property, (val) => b.Property = val);
proxyA.Property = 12; // mutates the proxied `a` as well

অতিরিক্ত হিসাবে, আপনি এ বা বি এর উদাহরণ থেকে লামডাপ্রক্সি উদাহরণগুলি তৈরি করতে সাধারণ সহায়ক লিখতে পারেন তারা আপনাকে "সাবলীল" স্টাইল দেওয়ার জন্য বর্ধিত পদ্ধতিও হতে পারে:

public static class ProxyExtension
{
    public static IProxy Proxied(this A a)
    {
      return new LambdaProxy(() => a.Property, (val) => a.Property = val);
    }

    public static IProxy Proxied(this B b)
    {
      return new LambdaProxy(() => b.Property, (val) => b.Property = val);
    }
}

এবং এখন প্রক্সি নির্মাণ এটির মতো দেখাচ্ছে:

IProxy proxyA = new A().Proxied();
IProxy proxyB = new B().Proxied();

আপনার কারখানার জন্য, আমি দেখতে পেয়েছি আপনি যদি এটি একটি "প্রধান" কারখানার পদ্ধতিতে রিফ্যাক্টর করতে পারেন যা একটি আইপ্রোক্সি গ্রহণ করে এবং এতে এবং অন্যান্য পদ্ধতিগুলির মধ্যে সমস্ত যুক্তি সম্পাদন করে যা কেবল প্রবেশ করে new A().Proxied()বা new B().Proxied():

public class LogicToBeApplied
{
    public A CreateA() {
      A a = new A();
      InitializeProxy(a.Proxied());
      return a; // or maybe return the proxy if you'd rather use that
    }

    public B CreateB() {
      B b = new B();
      InitializeProxy(b.Proxied());
      return b;
    }

    private void InitializeProxy(IProxy proxy)
    {
        proxy.IntProperty = 50;
    }
}

সি # তে আপনার সি ++ কোডের সমতুল্য করার কোনও উপায় নেই কারণ সি ++ টেম্পলেটগুলি কাঠামোগত টাইপিংয়ের উপর নির্ভর করে । যতক্ষণ না দুটি ক্লাসের একই পদ্ধতির নাম এবং স্বাক্ষর থাকে, সি ++ এ আপনি উভয়কেই সেই পদ্ধতিটি উদারভাবে কল করতে পারেন। সি # আছে নামমাত্র টাইপিং রয়েছে - শ্রেণি বা ইন্টারফেসের নাম এর ধরণের একটি অংশ। অতএব, ক্লাসগুলি Aএবং Bউত্তরাধিকার বা ইন্টারফেস প্রয়োগের মাধ্যমে সুস্পষ্ট "একটি" সম্পর্কের সংজ্ঞা দেওয়া না হলে যে কোনও ক্ষমতাতে একইরূপে আচরণ করা যাবে না।

প্রতি ক্লাসে এই পদ্ধতিগুলি প্রয়োগের বয়লারপ্লেট যদি খুব বেশি হয় তবে আপনি কোনও ফাংশন লিখতে পারেন যা কোনও বস্তু নেয় এবং and করে LambdaProxyএবং একটি নির্দিষ্ট সম্পত্তির নাম সন্ধান করে প্রতিফলিতভাবে একটি তৈরি করতে পারে:

public class ReflectiveProxier 
{
    public object proxyReflectively(object proxied)
    {
        PropertyInfo prop = proxied.GetType().GetProperty("Property");
        return new LambdaProxy(
            () => prop.GetValue(proxied),
            (val) => prop.SetValue(proxied, val));
     }
}

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

এই কাজ করার কারণটির একটি অংশ LambdaProxyহ'ল "সর্বাধিক জেনেরিক"; এটি আইপ্রোক্সি চুক্তির "স্পিরিট" প্রয়োগকারী যে কোনও মানকে অভিযোজিত করতে পারে কারণ ল্যাম্বডাপ্রক্সির বাস্তবায়ন প্রদত্ত গিটার এবং সেটার ফাংশন দ্বারা সম্পূর্ণরূপে সংজ্ঞায়িত। ক্লাসগুলির সম্পত্তির জন্য আলাদা আলাদা নাম রয়েছে, বা বিভিন্ন ধরণের যেগুলি বোধগম্যভাবে এবং সুরক্ষিতভাবে উপস্থাপনযোগ্য int, বা Propertyশ্রেণীর অন্য কোনও বৈশিষ্ট্যের প্রতিনিধিত্ব করার ধারণাটি ম্যাপ করার কোনও উপায় রয়েছে তা এমনকি এটি কাজ করে। ফাংশনগুলির দ্বারা সরবরাহিত ইন্ডিরেশন আপনাকে সর্বাধিক নমনীয়তা দেয়।


একটি খুব আকর্ষণীয় পদ্ধতির, এবং অবশ্যই ফাংশনগুলি কল করার জন্য ব্যবহার করা যেতে পারে, তবে এটি কি কারখানার জন্য ব্যবহার করা যেতে পারে, যেখানে আমার আসলে ক্লাসএ এবং ক্লাসবি অবজেক্ট তৈরি করা দরকার?
ভ্লাদিমির স্টোকিক

@ ভ্লাদিমিরসটিক সম্পাদনাগুলি দেখুন, আমি এটির কিছুটা প্রসারিত করেছি
জ্যাক

অবশ্যই এই পদ্ধতিটি এখনও আপনার ম্যাপিং ফাংশন বগি থাকলে রানটাইম ত্রুটির অতিরিক্ত সম্ভাবনা সহ প্রতিটি প্রকারের জন্য স্পষ্টভাবে সম্পত্তি মানচিত্রের প্রয়োজন
Ewan

বিকল্প হিসাবে ReflectiveProxier, আপনি dynamicকীওয়ার্ড ব্যবহার করে একটি প্রক্সি তৈরি করতে পারেন ? আমার কাছে মনে হচ্ছে আপনার একই মৌলিক সমস্যা রয়েছে (যেমন ত্রুটিগুলি যা কেবল রানটাইম সময় ধরা পড়ে) তবে সিনট্যাক্স এবং রক্ষণাবেক্ষণ অনেক সহজ হবে।
ববসন

1
@ জ্যাক - যথেষ্ট মেলা আমি নিজের উত্তরটি ডেমোয়েং করে যুক্ত করেছি। নির্দিষ্ট বিরল পরিস্থিতিতে (এটির মতো) এটি এটি খুব দরকারী বৈশিষ্ট্য।
ববসন

12

এ এবং / বা বি থেকে উত্তরাধিকারসূত্রে ছাড়াই অ্যাডাপ্টারগুলি কীভাবে ব্যবহার করবেন সেগুলি এখানে বিদ্যমান A এবং B অবজেক্টগুলির জন্য ব্যবহারের সম্ভাবনার সাথে এখানে একটি রূপরেখা দেওয়া হল:

interface IAdapter
{
    int Property { get; set; }
}

class LogicToBeApplied<T> where T : IAdapter, new()
{
    public T Create()
    {
        var ret = new T();
        ret.Property = 50;
        return ret;
    }
}

class AAdapter : IAdapter
{
    A _a;

    public AAdapter()  // use this if you want to have the "logic" part create new objects
    {
        _a=new A();
    }

    public AAdapter(A a) // if you need an adapter for an existing object afterwards
    {
       _a=a;
    }

    public int Property
    {
        get { return _a.Property; }
        set { _a.Property = value; }
    }

    public A {get{return _a; } } // to provide access for non-generic code
}

class BAdapter 
{
     // analogously
}

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


কেন new int Property? আপনি কিছু ছায়া না।
গোলাপফ্লোয়েডএক্স 33

@ পিঙ্কফ্লয়েডএক্স 33: কেবল একটি টাইপো, এটিকে পরিবর্তন করেছে, ধন্যবাদ।
ডক ব্রাউন

9

আপনি অভিযোজিত ClassAএবং ClassBএকটি সাধারণ ইন্টারফেস মাধ্যমে। এইভাবে আপনার কোড LogicAToBeAppliedএকই থাকে। আপনার কাছে যা আছে তার চেয়ে খুব বেশি আলাদা নয়।

class A
{
    public int Property { get; set; }
}
class B
{
    public int Property { get; set; }
}

interface IAdapter
{
    int Property { get; set; }
}

class LogicToBeApplied<T> where T : IAdapter, new()
{
    public T Create()
    {
        var ret = new T();
        ret.Property = 50;
        return ret;
    }
}

class AAdapter : A, IAdapter { }

class BAdapter : B, IAdapter { }

1
অ্যাডাপ্টার প্যাটার্ন ব্যবহার করে +1 হ'ল এখানে traditionalতিহ্যবাহী ওওপি সমাধান। এটা একটা প্রক্সি চেয়ে একটা এডাপটার আরো যেহেতু আমরা খাপ খাওয়ানো হচ্ছে A, Bএকটি সাধারণ ইন্টারফেসে ধরনের। বড় সুবিধাটি হ'ল আমাদের সাধারণ যুক্তিকে নকল করতে হবে না। অসুবিধাটি হ'ল যুক্তি এখন আসল প্রকারের পরিবর্তে মোড়ক / প্রক্সি ইনস্ট্যান্ট করে।
আমন

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

@ ডকব্রাউন কীভাবে এই বিশেষ ক্ষেত্রে যাবে?
ভ্লাদিমির স্টোকিক

1
@ জ্যাক: এই ধরণের সমাধানটি যখন LogicToBeAppliedএকটি নির্দিষ্ট জটিলতা রয়েছে তখন অর্থবোধ করে এবং কোনও অবস্থাতেই কোড বেজে দুটি জায়গায় পুনরাবৃত্তি করা উচিত নয়। তারপরে অতিরিক্ত বয়লারপ্লেট কোডটি প্রায়শই নগণ্য।
ডক ব্রাউন

1
@ জ্যাক নিরর্থকতা কোথায়? দুটি শ্রেণীর একটি সাধারণ ইন্টারফেস নেই। আপনি চাদরে যে তৈরি না একটি সাধারণ ইন্টারফেস আছে। আপনি আপনার যুক্তি বাস্তবায়নের জন্য সেই সাধারণ ইন্টারফেসটি ব্যবহার করেন। এটি এমনটি নয় যে সি ++ কোডে একই রকম অপ্রয়োজনীয়তা বিদ্যমান না - এটি কোড প্রজন্মের কিছুটা পিছনে লুকানো। আপনি যে দৃঢ়ভাবে সম্পর্কে যা কিছু যদি মনে করেন চেহারা একই, যদিও তারা নয় একই, আপনি সবসময় T4S অথবা অন্য কোনো টেমপ্লেট সিস্টেম ব্যবহার করতে পারেন।
লুয়ান

8

সি ++ সংস্করণটি কেবলমাত্র কাজ করে কারণ এর টেমপ্লেটগুলি "স্ট্যাটিক হাঁসের টাইপিং" ব্যবহার করে - টাইপটি যতক্ষণ না সঠিক নাম সরবরাহ করে ততক্ষণ কোনও কিছু সংকলিত হয়। এটি আরও ম্যাক্রো সিস্টেমের মতো। সি # এবং অন্যান্য ভাষার জেনেরিক সিস্টেম খুব আলাদাভাবে কাজ করে।

ডিভনুল এবং ডক ব্রাউন এর উত্তরগুলি দেখায় যে কীভাবে অ্যাডাপ্টারের প্যাটার্নটি আপনার অ্যালগোরিদমকে সাধারণ রাখতে ব্যবহার করা যেতে পারে এবং এখনও কয়েকটা সীমাবদ্ধতার সাথে ... নির্বিচারে ধরণের কাজ করে। উল্লেখযোগ্যভাবে, আপনি এখন নিজের চেয়ে আলাদা ধরণের তৈরি করছেন।

কিছুটা কৌতুকপূর্ণ ব্যবস্থার সাথে, কোনও পরিবর্তন ছাড়াই হুবহু হ্রাসযুক্ত ধরণটি ব্যবহার করা সম্ভব। যাইহোক, আমাদের এখন লক্ষ্য ধরণের সাথে সমস্ত ইন্টারঅ্যাকশন পৃথক ইন্টারফেসে বের করতে হবে। এখানে, এই ইন্টারঅ্যাকশনগুলি নির্মাণ ও সম্পত্তি নির্ধারণ:

interface IInteractions<T> {
  T Instantiate();
  void AssignProperty(T target, int value);
}

একটি ওওপি ব্যাখ্যায়, এটি কৌশল প্যাটার্নের উদাহরণ হবে জেনেরিকের সাথে মিশ্রিত হলেও ।

এরপরে এই ইন্টারঅ্যাকশনগুলি ব্যবহার করতে আমরা আপনার যুক্তিটি আবার লিখতে পারি:

public class LogicBToBeApplied<T>
{
    public T CreateElement(IInteractions<T> interactions)
    {
        T retVal = interactions.Instantiate();
        interactions.AssignProperty(retVal, 50);
        return retVal;
    }
}

মিথস্ক্রিয়া সংজ্ঞাগুলি দেখতে হবে:

class Interactions_ClassA : IInteractions<ClassA> {
  public override ClassA Instantiate() { return new ClassA(); }
  public override void AssignProperty(ClassA target, int value) { target.IntProperty = value; }
}

এই পদ্ধতির বড় অসুবিধা হ'ল প্রোগ্রামারকে যুক্তি কল করার সময় একটি মিথস্ক্রিয়া উদাহরণ লিখতে এবং পাস করতে হয়। এটি অ্যাডাপ্টার-প্যাটার্ন ভিত্তিক সমাধানগুলির সাথে মোটামুটি মিল, তবে কিছুটা সাধারণ।

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


আকর্ষণীয় পদ্ধতির। আমার প্রথম পছন্দটি এমনটি নয়, তবে আমি অনুমান করি যে কোনও ফ্রেমওয়ার্ক বা এর মতো কিছু লেখার সময় এর কিছু সুবিধা থাকতে পারে।
ডক ব্রাউন

8

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

using System;

namespace ConsoleApplication3
{
    class A
    {
        public int SomeProperty { get; set; }
    }

    class B
    {
        public int SomeProperty { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var a = new A();
            var b = new B();

            SetSomeProperty(a, 7);
            SetSomeProperty(b, 12);

            Console.WriteLine($"a.SomeProperty = {a.SomeProperty}, b.SomeProperty = {b.SomeProperty}");
        }

        static void SetSomeProperty(dynamic obj, int value)
        {
            obj.SomeProperty = value;
        }
    }
}

4

অন্য উত্তরগুলি সঠিকভাবে সমস্যা চিহ্নিত করে এবং কার্যক্ষম সমাধান সরবরাহ করে। সি # (সাধারণত) "হাঁসের টাইপিং" সমর্থন করে না ( "এটি যদি হাঁসের মতো চলতে থাকে ..."), সুতরাং আপনার বাধ্য করার ClassAএবং ClassBবিনিময়যোগ্য হওয়ার কোনও উপায় নেই যদি সেভাবে ডিজাইন না করা হত।

তবে, যদি আপনি ইতিমধ্যে রানটাইম ত্রুটির ঝুঁকি গ্রহণ করতে ইচ্ছুক হন তবে প্রতিবিম্বটি ব্যবহার করার চেয়ে সহজ উত্তর answer

সি # এর dynamicকীওয়ার্ড রয়েছে যা এই জাতীয় পরিস্থিতিতে উপযুক্ত। এটি সংকলককে বলে "রানটাইম হওয়া পর্যন্ত এটি কী ধরণের তা আমি জানব না (এবং সম্ভবত তখনও নয়), সুতরাং আমাকে এটিকে কিছু করার অনুমতি দিন "।

এটি ব্যবহার করে আপনি যে ফাংশনটি চান ঠিক তেমন নির্মাণ করতে পারেন:

public class LogicToBeApplied<T> where T : new()
{
    public static T CreateElement()
    {
        dynamic retVal = new T(); // This doesn't care what type T is.
        retVal.IntProperty = 50;  // This will fail at runtime if there is no "IntProperty" 
                                  // or it doesn't accept an int.
        return retVal;            // Once again, we don't care what it is.
    }
}

staticকীওয়ার্ডের ব্যবহারটিও লক্ষ্য করুন । এটি আপনাকে এটি হিসাবে এটি ব্যবহার করতে দেয়:

A classAElement = LogicToBeApplied<A>.CreateElement();
B classBElement = LogicToBeApplied<B>.CreateElement();

dynamicরিফ্লেকশনটি ব্যবহারের ওয়ান-টাইম হিট (এবং যুক্ত জটিলতা) যেভাবে রয়েছে তাতে কোনও বড় চিত্রের পারফরম্যান্সের প্রভাব নেই । আপনার কোডটি প্রথমবারের সাথে নির্দিষ্ট টাইপের সাথে ডায়নামিক কলটিতে অল্প পরিমাণে ওভারহেড পাবে তবে পুনরাবৃত্তি কলগুলি স্ট্যান্ডার্ড কোডের মতো দ্রুত হবে। যাইহোক, যদি আপনি হবে একটি পেতে RuntimeBinderExceptionযদি আপনি কিছু যে সম্পত্তি নেই মধ্যে পাস করার চেষ্টা করুন, এবং সেখানে ঠিক সেই এগিয়ে চেক করতে কোন ভাল উপায়। আপনি সম্ভবত বিশেষভাবে কার্যকরভাবে সেই ত্রুটিটি পরিচালনা করতে চান।


এটি ধীর হতে পারে, তবে প্রায়শই স্লো কোড কোনও সমস্যা হয় না।
ইয়ান

@ আইয়ান - ভাল পয়েন্ট আমি অভিনয়টি সম্পর্কে আরও কিছু যোগ করেছি added এটি যতটা খারাপ আপনি ভাবেন ঠিক তত খারাপ নয়, তবে আপনি একই জায়গাগুলিতে একই ক্লাস পুনরায় ব্যবহার করছেন।
ববসন

সি ++ টেম্পলেটগুলিতে মনে রাখবেন ভার্চুয়াল পদ্ধতিগুলির ওভারহেডও নেই!
ইয়ান

2

নামটি দিয়ে সম্পত্তিটি টানতে আপনি প্রতিবিম্বটি ব্যবহার করতে পারেন।

public class logic 
{
    public object getNew<T>() where T : new()
    {
        T ret = new T();
        try
        {
            var property = typeof(T).GetProperty("IntProperty");
            if (property != null && property.PropertyType == typeof(int))
            {
                property.SetValue(ret, 50);
            }
        }
        catch (AmbiguousMatchException)
        {
            //hmm..
        }
        return ret;
    }
}

স্পষ্টতই আপনি এই পদ্ধতিতে একটি রানটাইম ত্রুটি ঝুঁকিপূর্ণ। কোনটি সি # আপনার করা বন্ধ করার চেষ্টা করছে।

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

(আমি চেষ্টা করব এবং নিবন্ধটি খনন করব)

অন্য পদ্ধতি, যদিও আমি নিশ্চিত এটি আপনার কোনও কোড সংরক্ষণ করে না, এটি এ এবং বি উভয়কেই সাবক্লাস করা এবং ইন্টারপোপার্টি সহ একটি ইন্টারফেসের উত্তরাধিকারী হবে।

public interface IIntProp {
    public int IntProperty {get, set}
}

public class A2 : A, IIntProp {}

public class B2 : B, IIntProp {}

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

1
অবশ্যই আপনি আপনার সি ++ সমাধানের সাথে একই ঝুঁকি নিয়েছেন?
ইয়ান

4
@ ইভান নং, সি ++ সংকলনের সময় সদস্যের জন্য চেক করুন
কালেথ

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

1
@ জ্যাকটি ডাউনসাইডস রয়েছে, তবে বিবেচনা করুন যে ম্যাপার্স, সিরিয়ালাইজারস, ডিপেন্ডেন্সি ইনজেকশন ফ্রেমওয়ার্ক ইত্যাদিতে প্রতিবিম্বটি ব্যাপকভাবে ব্যবহৃত হয় এবং লক্ষ্যটি নূন্যতম পরিমাণের সাথে এটি করা
Ewan

0

আমি কেবল implicit operatorজ্যাকের উত্তরের প্রতিনিধি / ল্যাম্বদা পদ্ধতির সাথে একসাথে রূপান্তরগুলি ব্যবহার করতে চেয়েছিলাম । Aএবং Bহিসাবে অনুমান করা হয়:

// A and B are mutable reference types

class A
{
  public int IntProperty { get; set; }
}

class B
{
  public int IntProperty { get; set; }
}

তারপরে নিখুঁত ব্যবহারকারীর দ্বারা সংজ্ঞায়িত রূপান্তরগুলি (কোনও এক্সটেনশন পদ্ধতি বা অনুরূপ প্রয়োজন নেই) সহ দুর্দান্ত সিনট্যাক্স পাওয়া সহজ:

// Adapter is an immutable type. However, the delegate instances have a captured reference to an A or a B (closure semantics)
struct Adapter
{
  readonly Func<int> getter;
  readonly Action<int> setter;

  Adapter(Func<int> getter, Action<int> setter)
  {
    this.getter = getter;
    this.setter = setter;
  }

  public int IntProperty
  {
    get { return getter(); }
    set { setter(value); }
  }

  public static implicit operator Adapter(A a) => new Adapter(() => a.IntProperty, x => a.IntProperty = x);
  public static implicit operator Adapter(B b) => new Adapter(() => b.IntProperty, x => b.IntProperty = x);

  public A CloneToA() => new A { IntProperty = getter(), };
  public B CloneToB() => new B { IntProperty = getter(), };
}

ব্যবহারের উদাহরণ:

class LogicToBeApplied
{
  public static A CreateA()
  {
    var a = new A();
    Initialize(a);
    return a;
  }
  public static B CreateB()
  {
    var b = new B();
    Initialize(b);
    return b;
  }

  static void Initialize(Adapter a)
  {
    a.IntProperty = 50;
  }
}

Initializeপদ্ধতি দেখায় কিভাবে আপনার সাথে কাজ করতে Adapterকিনা এটি একটি হল বিষয়ে চিন্তা ছাড়া Aবা Bবা অন্য কিছু। Initializeপদ্ধতির অনুরোধগুলি দেখায় যে আমাদের .AsProxy()কংক্রিট Aবা Bএকটি হিসাবে আচরণ করার জন্য কোনও (দৃশ্যমান) cast ালাই বা এর মতো প্রয়োজন নেই Adapter

যদি ArgumentNullExceptionআর্গুমেন্টটি নাল রেফারেন্স হয় তবে আপনি ব্যবহারকারী-সংজ্ঞায়িত রূপান্তরগুলিতে একটি নিক্ষেপ করতে চান কিনা তা বিবেচনা করুন ।

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