c# - रचनाकारों का वारिस कैसे करें?




design inheritance (10)

क्या मुझे वास्तव में Foo से Bar और Bah में सभी Foo प्रतिलिपि बनाना है? और फिर यदि मैं Foo में एक कन्स्ट्रक्टर हस्ताक्षर बदलता हूं, तो क्या मुझे इसे Bar और Bah में अपडेट करना होगा?

हां, यदि आप उदाहरण बनाने के लिए रचनाकारों का उपयोग करते हैं।

क्या रचनाकारों का उत्तराधिकारी करने का कोई तरीका नहीं है?

नहीं।

क्या कोड पुन: उपयोग को प्रोत्साहित करने का कोई तरीका नहीं है?

खैर, मैं यह नहीं समझूंगा कि उत्तराधिकारी कन्स्ट्रक्टर एक अच्छी या बुरी चीज होगी और क्या यह कोड पुन: उपयोग को प्रोत्साहित करेगा, क्योंकि हमारे पास नहीं है और हम उन्हें नहीं प्राप्त करेंगे। :-)

लेकिन यहां 2014 में, वर्तमान सी # के साथ, आप इसके बजाय जेनेरिक create विधि का उपयोग कर विरासत वाले कन्स्ट्रक्टर की तरह कुछ प्राप्त कर सकते हैं। यह आपके बेल्ट में एक उपयोगी उपकरण हो सकता है, लेकिन आप इसके लिए हल्के ढंग से नहीं पहुंच पाएंगे। मैं हाल ही में इसके लिए पहुंचे जब कुछ सौ व्युत्पन्न कक्षाओं में उपयोग किए जाने वाले बेस प्रकार के कन्स्ट्रक्टर में कुछ पास करने की आवश्यकता के साथ सामना करना पड़ा (हाल ही में, आधार को किसी भी तर्क की आवश्यकता नहीं थी, और इसलिए डिफ़ॉल्ट कन्स्ट्रक्टर ठीक था - व्युत्पन्न कक्षाओं ने रचनाकारों को बिल्कुल घोषित नहीं किया, और स्वचालित रूप से आपूर्ति की गई एक)।

यह इस तरह दिख रहा है:

// In Foo:
public T create<T>(int i) where: where T : Foo, new() {
    T obj = new T();
    // Do whatever you would do with `i` in `Foo(i)` here, for instance,
    // if you save it as a data member;  `obj.dataMember = i;`
    return obj;
}

यह कहता है कि आप सामान्य प्रकार के फ़ंक्शन को एक प्रकार पैरामीटर का उपयोग करके कॉल कर सकते हैं जो Foo का कोई सबटाइप है जिसमें शून्य-तर्क कन्स्ट्रक्टर है।

फिर, Bar b new Bar(42) करने के बजाय, आप यह करेंगे:

var b = Foo.create<Bar>(42);
// or
Bar b = Foo.create<Bar>(42);
// or
var b = Bar.create<Bar>(42); // But you still need the <Bar> bit
// or
Bar b = Bar.create<Bar>(42);

वहां मैंने सीधे Foo पर create विधि को दिखाया है, लेकिन निश्चित रूप से यह किसी प्रकार की फैक्ट्री क्लास में हो सकता है, अगर यह सेटिंग उस फैक्ट्री क्लास द्वारा सेट की जा सकती है।

सिर्फ स्पष्टता के लिए: नाम create महत्वपूर्ण नहीं है, यह makeThingy या जो कुछ भी आपको पसंद हो सकता है।

पूर्ण उदाहरण

using System.IO;
using System;

class Program
{
    static void Main()
    {
        Bar b1 = Foo.create<Bar>(42);
        b1.ShowDataMember("b1");

        Bar b2 = Bar.create<Bar>(43); // Just to show `Foo.create` vs. `Bar.create` doesn't matter
        b2.ShowDataMember("b2");
    }

    class Foo
    {
        public int DataMember { get; private set; }

        public static T create<T>(int i) where T: Foo, new()
        {
            T obj = new T();
            obj.DataMember = i;
            return obj;
        }
    }

    class Bar : Foo
    {
        public void ShowDataMember(string prefix)
        {
            Console.WriteLine(prefix + ".DataMember = " + this.DataMember);
        }
    }
}

कई रचनाकारों और वर्चुअल विधि के साथ एक बेस क्लास की कल्पना करें

public class Foo
{
   ...
   public Foo() {...}
   public Foo(int i) {...}
   ...
   public virtual void SomethingElse() {...}
   ...
}

और अब मैं एक वंश वर्ग बनाना चाहता हूं जो वर्चुअल विधि को ओवरराइड करता है:

public class Bar : Foo 
{
   public override void SomethingElse() {...}
}

और एक और वंशज जो कुछ और सामान करता है:

public class Bah : Bar
{
   public void DoMoreStuff() {...}
}

क्या मुझे वास्तव में फू से बार और बह में सभी रचनाकारों की प्रतिलिपि बनाना है? और फिर यदि मैं फू में एक कन्स्ट्रक्टर हस्ताक्षर बदलता हूं, तो क्या मुझे इसे बार और बह में अपडेट करना होगा?

क्या रचनाकारों का उत्तराधिकारी करने का कोई तरीका नहीं है? क्या कोड पुन: उपयोग को प्रोत्साहित करने का कोई तरीका नहीं है?


387 कन्स्ट्रक्टर ?? यह तुम्हारी मुख्य समस्या है। इसके बजाय इसके बारे में कैसे?

public Foo(params int[] list) {...}

एक और सरल समाधान संरचना या सरल डेटा वर्ग का उपयोग करना हो सकता है जिसमें गुणों के रूप में पैरामीटर शामिल हैं; इस तरह आप एक ही कन्स्ट्रक्टर पैरामीटर के रूप में "पैरामीटर क्लास" को पार करते हुए, समय से पहले सेट किए गए सभी डिफ़ॉल्ट मान और व्यवहार कर सकते हैं:

public class FooParams
{
    public int Size...
    protected myCustomStruct _ReasonForLife ...
}
public class Foo
{
    private FooParams _myParams;
    public Foo(FooParams myParams)
    {
          _myParams = myParams;
    }
}

यह एकाधिक रचनाकारों (कभी-कभी) की गड़बड़ी से बचाता है और पैरामीटर सरणी द्वारा प्रदान नहीं किए गए मजबूत टाइपिंग, डिफ़ॉल्ट मान और अन्य लाभ देता है। यह आगे बढ़ना आसान बनाता है क्योंकि फू से विरासत में आने वाली किसी भी चीज को अभी भी फूपरम की आवश्यकता हो सकती है, या यहां तक ​​कि जोड़ सकते हैं। आपको अभी भी कन्स्ट्रक्टर की प्रतिलिपि बनाने की आवश्यकता है, लेकिन आप हमेशा (अधिकांश समय) केवल (सामान्य नियम के रूप में) (कम से कम, अभी के लिए) को एक कन्स्ट्रक्टर की आवश्यकता होती है।

public class Bar : Foo
{
    public Bar(FooParams myParams) : base(myParams) {}
}

मुझे वास्तव में ओवरलोडेड इनटाइलाइज () और क्लास फैक्टरी पैटर्न बेहतर लगता है, लेकिन कभी-कभी आपको केवल एक स्मार्ट कन्स्ट्रक्टर की आवश्यकता होती है। सिर्फ एक विचार।


चूंकि Foo एक वर्ग है, क्या आप वर्चुअल ओवरलोडेड Initialise() विधियों को नहीं बना सकते हैं? फिर वे उप-वर्गों के लिए उपलब्ध होंगे और अभी भी विस्तार योग्य होंगे?

public class Foo
{
   ...
   public Foo() {...}

   public virtual void Initialise(int i) {...}
   public virtual void Initialise(int i, int i) {...}
   public virtual void Initialise(int i, int i, int i) {...}
   ... 
   public virtual void Initialise(int i, int i, ..., int i) {...}

   ...

   public virtual void SomethingElse() {...}
   ...
}

इसमें उच्च प्रदर्शन लागत नहीं होनी चाहिए जबतक कि आपके पास बहुत से डिफ़ॉल्ट संपत्ति मूल्य नहीं हैं और आपने इसे बहुत मारा है।


बहुत बुरा हम संकलक को स्पष्ट बताने के लिए मजबूर हैं:

Subclass(): base() {}
Subclass(int x): base(x) {}
Subclass(int x,y): base(x,y) {}

मुझे केवल 12 सबक्लास में 3 कन्स्ट्रक्टर करने की ज़रूरत है, इसलिए यह कोई बड़ा सौदा नहीं है, लेकिन मुझे इतने लंबे समय तक लिखने के लिए उपयोग किए जाने के बाद, हर सबक्लास पर दोहराने का बहुत शौक नहीं है। मुझे यकीन है कि इसके लिए एक वैध कारण है, लेकिन मुझे नहीं लगता कि मुझे कभी ऐसी समस्या का सामना करना पड़ा है जिसके लिए इस तरह के प्रतिबंध की आवश्यकता है।


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


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

इस तरह हम केवल उन सभी रचनाकारों को जोड़ने के बजाय चाहते हैं जो कम से कम दृश्यता - यानी निजी) के साथ ओवरराइड करते हैं। डेल्फी इस तरह से करता है, और मुझे याद आती है।

उदाहरण के लिए लें यदि आप System.IO.StreamWriter क्लास को ओवरराइड करना चाहते हैं, तो आपको अपनी नई कक्षा में सभी 7 कन्स्ट्रक्टर जोड़ने की ज़रूरत है, और यदि आपको टिप्पणी करना पसंद है तो आपको हेडर एक्सएमएल के साथ प्रत्येक को टिप्पणी करने की आवश्यकता है। इसे और भी खराब बनाने के लिए, मेटाडेटा दृश्य ने एक्सएमएल टिप्पणियों को उचित एक्सएमएल टिप्पणियों के रूप में रखा है, इसलिए हमें लाइन से लाइन जाना है और उन्हें कॉपी और पेस्ट करना है। माइक्रोसॉफ्ट यहाँ क्या सोच रहा था?

मैंने वास्तव में एक छोटी उपयोगिता लिखी है जहां आप मेटाडेटा कोड में पेस्ट कर सकते हैं और यह इसे ओवरराइड दृश्यता का उपयोग करके एक्सएमएल टिप्पणियों में परिवर्तित कर देगा।


समस्या यह नहीं है कि बार और बह को 387 रचनाकारों की प्रतिलिपि बनाना है, समस्या यह है कि फू के 387 रचनाकार हैं। फू स्पष्ट रूप से बहुत सी चीजें करता है - त्वरित प्रतिक्रियाक! साथ ही, जब तक आपके पास कन्स्ट्रक्टर में मूल्य निर्धारित करने का वास्तव में अच्छा कारण नहीं है (जो, यदि आप पैरामीटर रहित कन्स्ट्रक्टर प्रदान करते हैं, तो शायद आप नहीं करते हैं), मैं संपत्ति प्राप्त करने / सेटिंग का उपयोग करने की अनुशंसा करता हूं।


हां, आपको सभी 387 रचनाकारों की प्रतिलिपि बनाना है। आप उन्हें पुनर्निर्देशित करके कुछ पुन: उपयोग कर सकते हैं:

  public Bar(int i): base(i) {}
  public Bar(int i, int j) : base(i, j) {}

लेकिन यह सबसे अच्छा है जो आप कर सकते हैं।


public class BaseClass
{
    public BaseClass(params int[] parameters)
    {

    }   
}

public class ChildClass : BaseClass
{
    public ChildClass(params int[] parameters)
        : base(parameters)
    {

    }
}






inheritance