सी # जेनरिक - निरर्थक विधि से कैसे बचें?


28

मान लें कि मेरे पास दो कक्षाएं हैं जो इस तरह दिखती हैं (कोड का पहला ब्लॉक और सामान्य समस्या सी # से संबंधित है):

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();   

इस तरह मैं एकल जेनेरिक फैक्ट्री क्लास बना सकता था जो क्लासए और क्लासबी दोनों के लिए काम करेगा।

हालाँकि, C # में, मुझे दो अलग-अलग 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
@ लुअन यह अमूर्त कारखाना पैटर्न की भिन्नता का एक सरल उदाहरण है। कल्पना कीजिए कि दर्जनों वर्ग हैं जो ClassA या ClassB को विरासत में देते हैं, और वह ClassA और ClassB अमूर्त वर्ग हैं। इनहेरिट की गई कक्षाओं में कोई अतिरिक्त जानकारी नहीं होती है, और उन्हें तत्काल करने की आवश्यकता होती है। उनमें से प्रत्येक के लिए एक कारखाना लिखने के बजाय, मैंने जेनरिक का उपयोग करने का विकल्प चुना।
व्लादिमीर स्टोकेक

6
ठीक है, आप प्रतिबिंब या गतिशीलता का उपयोग कर सकते हैं यदि आपको विश्वास है कि वे भविष्य के रिलीज में इसे तोड़ने नहीं जा रहे हैं।
केसी

यह वास्तव में जेनरिक के बारे में मेरी सबसे बड़ी शिकायत है कि यह ऐसा नहीं कर सकता।
जोशुआ

1
@ जोशुआ, मैं इसे "डक टाइपिंग" का समर्थन नहीं करने वाले इंटरफेस के साथ एक मुद्दे के रूप में अधिक सोचता हूं।
इयान

जवाबों:


49

एक प्रॉक्सी इंटरफ़ेस जोड़ें (कभी-कभी एक एडाप्टर कहा जाता है , कभी-कभी सूक्ष्म अंतर के साथ), LogicToBeAppliedप्रॉक्सी के संदर्भ में लागू करें , फिर दो लैम्ब्डा से इस प्रॉक्सी के उदाहरण का निर्माण करने का एक तरीका जोड़ें: संपत्ति के लिए एक और सेट के लिए एक।

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();

अपने कारखाने के रूप में, मैं अगर आप इसे एक "मुख्य" कारखाने विधि है कि एक IProxy और प्रदर्शन यह और अन्य तरीकों पर सभी तर्क है कि बस में पास स्वीकार करता है में refactor कर सकते हैं देखना चाहते हैं 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;
    }
}

C # में अपने C ++ कोड के बराबर करने का कोई तरीका नहीं है क्योंकि C ++ टेम्प्लेट संरचनात्मक टाइपिंग पर निर्भर करता है । जब तक दो वर्गों में एक ही विधि का नाम और हस्ताक्षर है, तब तक C ++ में आप उन दोनों पर उदारतापूर्वक उस विधि को कॉल कर सकते हैं। C # में नाममात्र टाइपिंग है - एक वर्ग या इंटरफ़ेस का नाम इसके प्रकार का हिस्सा है। इसलिए, वर्गों Aऔर Bकिसी भी क्षमता में एक ही नहीं माना जा सकता, जब तक कि कोई स्पष्ट संबंध या तो विरासत या इंटरफेस कार्यान्वयन के माध्यम से परिभाषित किया गया है "एक है।"

यदि प्रति कक्षा इन विधियों को लागू करने का बॉयलरप्लेट बहुत अधिक है, तो आप एक फ़ंक्शन लिख सकते हैं जो एक वस्तु लेता है और एक विशिष्ट संपत्ति के नाम की तलाश करके प्रतिबिंबित करता है 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));
     }
}

गलत प्रकार की वस्तुओं को दिए जाने पर यह असामान्य रूप से विफल हो जाता है; प्रतिबिंब स्वाभाविक रूप से विफलताओं की संभावना का परिचय देता है C # प्रकार प्रणाली को रोक नहीं सकता है। सौभाग्य से आप प्रतिबिंब से बच सकते हैं जब तक कि सहायकों का रखरखाव बोझ बहुत अधिक नहीं हो जाता है क्योंकि आपको चिंतनशील चीनी जोड़ने के लिए IProxy इंटरफ़ेस या LambdaProxy कार्यान्वयन को संशोधित करने की आवश्यकता नहीं है।

इस काम का कारण यह है कि LambdaProxy"अधिकतम सामान्य" है; यह IProxy अनुबंध की "आत्मा" को लागू करने वाले किसी भी मूल्य को अनुकूलित कर सकता है क्योंकि लैम्ब्डाप्रॉक्सी का कार्यान्वयन पूरी तरह से दिए गए गेटटर और सेटर फ़ंक्शन द्वारा परिभाषित किया गया है। यह तब भी काम करता है जब वर्गों के पास संपत्ति के लिए अलग-अलग नाम हैं, या विभिन्न प्रकार जो समझदारी से और सुरक्षित रूप से intएस के रूप में प्रतिनिधित्व करने योग्य हैं , या यदि अवधारणा को मैप करने का कोई तरीका Propertyहै जो कि कक्षा की किसी भी अन्य विशेषताओं का प्रतिनिधित्व करने वाला है। कार्यों द्वारा प्रदान की गई अप्रत्यक्षता आपको अधिकतम लचीलापन देती है।


एक बहुत ही दिलचस्प दृष्टिकोण, और निश्चित रूप से फ़ंक्शन को कॉल करने के लिए उपयोग किया जा सकता है, लेकिन क्या इसका उपयोग कारखाने के लिए किया जा सकता है, जहां मुझे वास्तव में क्लास और क्लासबी की वस्तुओं को बनाने की आवश्यकता है?
व्लादिमीर स्टोक

@VladimirStokic संपादन देखें, मैंने इस पर थोड़ा विस्तार किया है
जैक

निश्चित रूप से यह विधि अभी भी आपको एक रनटाइम त्रुटि की अतिरिक्त संभावना के साथ प्रत्येक प्रकार की संपत्ति को स्पष्ट रूप से मैप करने की आवश्यकता है यदि आपका मैपिंग फ़ंक्शन छोटी है
ईवान

के विकल्प के रूप में ReflectiveProxier, क्या आप dynamicकीवर्ड का उपयोग करके प्रॉक्सी का निर्माण कर सकते हैं ? मुझे लगता है कि आपको एक ही मौलिक समस्याएं होंगी (यानी त्रुटियां जो केवल रनटाइम में पकड़ी जाती हैं), लेकिन वाक्यविन्यास और रखरखाव बहुत सरल होगा।
बोबसन

1
@ जेक - पर्याप्त मेला। मैंने अपना खुद का जवाब जोड़ दिया। यह एक बहुत ही उपयोगी विशेषता है, कुछ दुर्लभ परिस्थितियों में (जैसे यह)।
बोबसन 23

12

यहाँ मौजूदा ए और बी वस्तुओं के लिए उपयोग करने की संभावना के साथ ए और / या बी से विरासत में लिए बिना एडेप्टर का उपयोग करने की एक रूपरेखा है:

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? आप कुछ भी छाया नहीं कर रहे हैं।
गुलाबीफ्लोक्स 3333

@ pinkfloydx33: बस एक टाइपो, इसे बदल दिया, धन्यवाद।
डॉक्टर ब्राउन

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 यहां पारंपरिक ओओपी समाधान है। जब हम एक सामान्य इंटरफ़ेस में टाइप करते हैं A, तो यह एक प्रॉक्सी की तुलना में अधिक Bहोता है। बड़ा फायदा यह है कि हमें आम लॉजिक की नकल नहीं करनी है। नुकसान यह है कि तर्क अब वास्तविक प्रकार के बजाय आवरण / छद्म का तात्पर्य करता है।
आमोन

5
इस समाधान के साथ समस्या यह है कि आप बस टाइप ए और बी की दो वस्तुओं को नहीं ले सकते हैं, उन्हें किसी भी तरह से एप्रोक्सी और बीप्रॉक्सी में बदल सकते हैं, और फिर उनके लिए लॉजिकटेब लागू कर सकते हैं। इस समस्या को वंशानुक्रम के बजाय एकत्रीकरण का उपयोग करके हल किया जा सकता है (ए और बी से व्युत्पन्न नहीं बल्कि प्रॉक्सी ए और बी ऑब्जेक्ट का संदर्भ लेकर) प्रॉक्सी वस्तुओं को लागू करें (देखें। विरासत का गलत उपयोग कैसे समस्याओं का कारण बनता है इसका एक उदाहरण फिर से।
डॉक्टर ब्राउन

@DocBrown इस विशेष मामले में कैसे जाना होगा?
व्लादिमीर स्टोक

1
@ जैक: LogicToBeAppliedएक निश्चित जटिलता होने पर इस तरह का समाधान समझ में आता है और किसी भी परिस्थिति में कोड बेस में दो स्थानों पर दोहराया नहीं जाना चाहिए। फिर अतिरिक्त बॉयलरप्लेट कोड अक्सर नगण्य होता है।
Doc Brown

1
@Jack अतिरेक कहाँ है? दो वर्गों में एक सामान्य इंटरफ़ेस नहीं है। आप ऐसे रैपर बनाते हैं जिनमें एक सामान्य इंटरफ़ेस होता है। आप अपने तर्क को लागू करने के लिए उस सामान्य इंटरफ़ेस का उपयोग करते हैं। ऐसा नहीं है कि समान अतिरेक C ++ कोड में मौजूद नहीं है - यह कोड पीढ़ी के थोड़ा पीछे छिपा है। यदि आपको लगता है कि दृढ़ता से उन चीजों के बारे में जो समान दिखती हैं , भले ही वे समान न हों , तो आप हमेशा T4s या किसी अन्य टेम्पल सिस्टम का उपयोग कर सकते हैं।
लुआवन

8

C ++ संस्करण केवल इसलिए काम करता है क्योंकि इसके टेम्प्लेट "स्टैटिक डक टाइपिंग" का उपयोग करते हैं - जब तक टाइप सही नाम प्रदान करता है तब तक कुछ भी संकलन करता है। यह मैक्रो सिस्टम की तरह अधिक है। C # और अन्य भाषाओं की जेनरिक प्रणाली बहुत अलग तरीके से काम करती है।

devnull's और Doc Brown के उत्तरों से पता चलता है कि आपके एल्गोरिथ्म को सामान्य रखने के लिए एडेप्टर पैटर्न का उपयोग कैसे किया जा सकता है, और फिर भी कुछ प्रकार के प्रतिबंधों के साथ मनमाने प्रकारों पर काम कर सकते हैं। विशेष रूप से, अब आप वास्तव में चाहते हैं की तुलना में एक अलग प्रकार बना रहे हैं।

थोड़ी सी चालाकी के साथ, बिना किसी बदलाव के बिल्कुल इच्छित प्रकार का उपयोग करना संभव है। हालांकि, अब हमें लक्ष्य प्रकार के साथ सभी इंटरैक्शन को एक अलग इंटरफ़ेस में निकालने की आवश्यकता है। यहां, ये इंटरैक्शन निर्माण और संपत्ति असाइनमेंट हैं:

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; }
}

इस दृष्टिकोण का बड़ा नुकसान यह है कि प्रोग्रामर को लॉजिक कॉल करते समय इंटरेक्शन इंस्टेंस लिखना और पास करना पड़ता है। यह एडाप्टर-पैटर्न आधारित समाधानों के समान है, लेकिन थोड़ा अधिक सामान्य है।

मेरे अनुभव में, यह निकटतम है जो आप अन्य भाषाओं में टेम्पलेट फ़ंक्शन के लिए प्राप्त कर सकते हैं। इसी तरह की तकनीकों का उपयोग हास्केल, स्काला, गो और रस्ट में एक प्रकार की परिभाषा के बाहर इंटरफेस को लागू करने के लिए किया जाता है। हालाँकि, इन भाषाओं में संकलक कदम और सही अंतःक्रियात्मक उदाहरण का चयन करता है ताकि आप वास्तव में अतिरिक्त तर्क न देख सकें। यह भी C # की एक्सटेंशन विधियों के समान है, लेकिन यह स्थिर विधियों तक सीमित नहीं है।


दिलचस्प दृष्टिकोण। वह नहीं जो मेरी पहली पसंद होगी, लेकिन मुझे लगता है कि यह एक फ्रेमवर्क या ऐसा कुछ लिखते समय कुछ फायदे हो सकते हैं।
डॉक्टर ब्राउन

8

यदि आप वास्तव में हवा को सावधानी से फेंकना चाहते हैं, तो आप "गतिशील" का उपयोग कर सकते हैं ताकि कंपाइलर आपके लिए सभी प्रतिबिंब नस्टनेस का ध्यान रख सके। यदि आप SetSomeProperty के पास कोई ऑब्जेक्ट पास करते हैं, जिसके पास SomeProperty नाम की कोई प्रॉपर्टी नहीं है, तो यह रनटाइम त्रुटि के कारण होगा।

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

अन्य उत्तर सही ढंग से समस्या की पहचान करते हैं और व्यावहारिक समाधान प्रदान करते हैं। C # (आमतौर पर) "बतख टाइपिंग" का समर्थन नहीं करता है ("यदि यह एक बतख की तरह चलता है ..."), तो आपके पास उस तरह से डिज़ाइन नहीं किए जाने पर आपके बल ClassAऔर ClassBविनिमेय होने का कोई तरीका नहीं है।

हालांकि, यदि आप पहले से ही रनटाइम फॉल्ट के जोखिम को स्वीकार करने के लिए तैयार हैं, तो रिफ्लेक्शन का उपयोग करने से आसान उत्तर है।

C # में 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अगर आप कुछ है जो कि संपत्ति नहीं है में पारित करने के लिए प्रयास करते हैं, और वहाँ समय की है कि आगे की जांच करने के लिए कोई अच्छा तरीका है। आप विशेष रूप से उस त्रुटि को एक उपयोगी तरीके से संभाल सकते हैं।


यह धीमा हो सकता है, लेकिन अक्सर धीमा कोड कोई समस्या नहीं है।
इयान

@ इयान - अच्छी बात है। मैंने प्रदर्शन के बारे में थोड़ा और जोड़ा। यह वास्तव में उतना बुरा नहीं है जितना कि आप सोचते हैं, बशर्ते आप एक ही वर्ग को एक ही स्पॉट में पुनः प्रयोग कर रहे हों।
बोबसन 23

याद रखें C ++ टेम्प्लेट में वर्चुअल तरीकों का ओवरहेड भी नहीं है!
इयान

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;
    }
}

जाहिर है आप इस विधि के साथ एक रनटाइम त्रुटि का जोखिम उठाते हैं। जो कि C # आपको करने से रोकने की कोशिश कर रहा है।

मैंने कहीं पढ़ा है कि C # का एक भविष्य संस्करण आपको एक इंटरफ़ेस के रूप में ऑब्जेक्ट पास करने की अनुमति देगा, जो उन्हें विरासत में नहीं मिलता है, लेकिन मेल खाता है। जिससे आपकी समस्या का समाधान भी होगा।

(मैं कोशिश करूँगा और लेख को खोदूंगा)

एक अन्य विधि, हालांकि मुझे यकीन नहीं है कि यह आपको किसी भी कोड को बचाता है, यह ए और बी दोनों को उप-वर्ग करने के लिए होगा और इंट्रॉपर्टी के साथ एक इंटरफ़ेस भी प्राप्त करेगा।

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

public class A2 : A, IIntProp {}

public class B2 : B, IIntProp {}

रनटाइम त्रुटि और प्रदर्शन के मुद्दों की संभावना वे कारण हैं जो मैं प्रतिबिंब के साथ नहीं जाना चाहता था। हालाँकि, आपके लेख में आपके द्वारा बताए गए लेख को पढ़ने में मुझे बहुत दिलचस्पी है। इसे पढ़ने की बेसब्री से प्रतीक्षा है।
व्लादिमीर स्टोक

1
निश्चित रूप से आप अपने सी ++ समाधान के साथ एक ही जोखिम लेते हैं?
आठ

4
@ ईवन नं, सी ++ सदस्य के लिए संकलन समय पर जाँच करता है
केलथ

परावर्तन का अर्थ है अनुकूलन समस्याओं और (अधिक महत्वपूर्ण बात) कठिन रनटाइम त्रुटियों के लिए। इनहेरिटेंस और एक सामान्य इंटरफ़ेस का मतलब है कि इन वर्गों में से हर एक के लिए समय से पहले एक उपवर्ग घोषित करना (मौके पर किसी को गुमनाम बनाने का कोई तरीका नहीं है) और हर बार एक ही संपत्ति के नाम का उपयोग नहीं करने पर काम नहीं करता है।
जैक

1
@ जेक डाउनसाइड हैं, लेकिन विचार करें कि प्रतिबिंब का इस्तेमाल बड़े पैमाने पर मैपर, सीरीएलाइज़र, डिपेंडेंसी इंजेक्शन फ्रेमवर्क आदि में किया जाता है और लक्ष्य यह है कि कोड डुप्लिकेट की कम से कम राशि के साथ ऐसा किया जाए
ईवान

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या कुछ और। की invokations Initializeविधि शो हम किसी भी (दृश्यमान) डाली या की जरूरत नहीं है कि .AsProxy()या इलाज ठोस के समान Aया Bएक के रूप में Adapter

विचार करें कि क्या आप ArgumentNullExceptionउपयोगकर्ता-परिभाषित रूपांतरणों में फेंकना चाहते हैं यदि पारित तर्क एक अशक्त संदर्भ है, या नहीं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.