c# - इंटरफेस एक कन्स्ट्रक्टर हस्ताक्षर परिभाषित?




interface constructor (11)

अंतःस्थापित रचनाकारों के साथ एक और समस्या का प्रदर्शन करने में बहुत देर से योगदान। (मैं इस सवाल का चयन करता हूं क्योंकि इसमें समस्या का स्पष्ट अभिव्यक्ति है)। मान लीजिए कि हम कर सकते हैं:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

class Person : IPerson, ICustomer
{
    Person(string name) { }
    Person(DateTime registrationDate) { }
}

जहां सम्मेलन द्वारा "इंटरफेस कन्स्ट्रक्टर" के कार्यान्वयन को टाइप नाम से बदल दिया जाता है।

अब एक उदाहरण बनाओ:

ICustomer a = new Person("Ernie");

क्या हम कहते हैं कि अनुबंध ICustomer का पालन किया जाता है?

और इसके बारे में क्या:

interface ICustomer
{
    ICustomer(string address);
}

यह अजीब बात है कि यह पहली बार है जब मैंने इस समस्या में टक्कर लगी है, लेकिन:

आप सी # इंटरफ़ेस में एक कन्स्ट्रक्टर को कैसे परिभाषित करते हैं?

संपादित करें
कुछ लोग एक उदाहरण चाहते थे (यह एक फ्री टाइम प्रोजेक्ट है, इसलिए हाँ, यह एक गेम है)

IDrawable
+ अद्यतन
+ ड्रा

अद्यतन करने में सक्षम होने के लिए (स्क्रीन के किनारे के लिए जांच करें आदि) और खुद को आकर्षित करें इसे हमेशा GraphicsDeviceManager आवश्यकता होगी। इसलिए मैं यह सुनिश्चित करना चाहता हूं कि ऑब्जेक्ट का संदर्भ हो। यह कन्स्ट्रक्टर में होगा।

अब जब मैंने इसे लिखा है, मुझे लगता है कि मैं यहां जो कार्यान्वित कर रहा हूं वह है IObservable और GraphicsDeviceManager को ध्यान देने IDrawable होना चाहिए ... ऐसा लगता है कि मुझे एक्सएनए फ्रेमवर्क नहीं मिलता है, या ढांचे को बहुत अच्छी तरह से नहीं सोचा जाता है।

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


आप जेनेरिक चाल के साथ ऐसा कर सकते हैं, लेकिन यह अभी भी जॉन स्कीट ने जो लिखा है उससे कमजोर है:

public interface IHasDefaultConstructor<T> where T : IHasDefaultConstructor<T>, new()
{
}

कक्षा जो इस इंटरफ़ेस को लागू करती है उसके पास पैरामीटर रहित कन्स्ट्रक्टर होना चाहिए:

public class A : IHasDefaultConstructor<A> //Notice A as generic parameter
{
    public A(int a) { } //compile time error
}

आप नहीं कर सकते

इंटरफ़ेस उन अनुबंधों को परिभाषित करता है जो अन्य ऑब्जेक्ट्स लागू करते हैं और इसलिए कोई ऐसा राज्य नहीं है जिसे प्रारंभ करने की आवश्यकता है।

यदि आपके पास कुछ राज्य है जिसे आरंभ करने की आवश्यकता है, तो आपको इसके बजाय एक सार आधार वर्ग का उपयोग करने पर विचार करना चाहिए।


आप नहीं करते

कन्स्ट्रक्टर कक्षा का हिस्सा है जो एक इंटरफ़ेस को कार्यान्वित कर सकता है। इंटरफ़ेस केवल उन तरीकों का अनुबंध है जो वर्ग को लागू करना चाहिए।


इस समस्या को हल करने का एक तरीका मैंने पाया कि निर्माण को अलग कारखाने में अलग करना है। उदाहरण के लिए मेरे पास IQueueItem नामक एक अमूर्त वर्ग है, और मुझे उस ऑब्जेक्ट को किसी अन्य ऑब्जेक्ट (क्लाउडक्यूयूमेसेज) से और उस अनुवाद का अनुवाद करने का एक तरीका चाहिए। तो इंटरफ़ेस IQueueItem पर मेरे पास है -

public interface IQueueItem
{
    CloudQueueMessage ToMessage();
}

अब, मुझे क्लाउड क्यूयू मैसेज को एक IQueueItem पर अनुवाद करने के लिए मेरी वास्तविक कतार कक्षा के लिए भी एक तरीका चाहिए - यानी IQueueItem objMessage = ItemType.FromMessage जैसे स्थिर निर्माण की आवश्यकता। इसके बजाय मैंने एक और इंटरफ़ेस IQueueFactory परिभाषित किया -

public interface IQueueItemFactory<T> where T : IQueueItem
{
    T FromMessage(CloudQueueMessage objMessage);
}

अब मैं अपनी जेनेरिक कतार कक्षा को नई () बाधा के बिना लिख ​​सकता हूं जो मेरे मामले में मुख्य मुद्दा था।

public class AzureQueue<T> where T : IQueueItem
{
    private IQueueItemFactory<T> _objFactory;
    public AzureQueue(IQueueItemFactory<T> objItemFactory)
    {
        _objFactory = objItemFactory;
    }


    public T GetNextItem(TimeSpan tsLease)
    {
        CloudQueueMessage objQueueMessage = _objQueue.GetMessage(tsLease);
        T objItem = _objFactory.FromMessage(objQueueMessage);
        return objItem;
    }
}

अब मैं एक उदाहरण बना सकता हूं जो मेरे लिए मानदंडों को पूरा करता है

 AzureQueue<Job> objJobQueue = new JobQueue(new JobItemFactory())

उम्मीद है कि यह किसी और को किसी दिन बाहर करने में मदद करता है, स्पष्ट रूप से समस्या और समाधान दिखाने के लिए बहुत सारे आंतरिक कोड को हटा दिया गया है


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

यद्यपि कुछ विकल्प हैं:

  • एक अमूर्त वर्ग बनाएं जो कम से कम डिफ़ॉल्ट कार्यान्वयन के रूप में कार्य करता है। उस वर्ग में ऐसे रचनाकार होना चाहिए जो आप कक्षाओं को लागू करने की अपेक्षा करते हैं।

  • यदि आप ओवरकिल पर ध्यान नहीं देते हैं, तो सार फैक्ट्री पैटर्न का उपयोग करें और फ़ैक्टरी क्लास इंटरफ़ेस में एक विधि घोषित करें जिसमें आवश्यक हस्ताक्षर हैं।

  • Update और Draw विधियों के पैरामीटर के रूप में GraphicsDeviceManager को पास करें।

  • उस ऑब्जेक्ट के हिस्से में GraphicsDeviceManager को पास करने के लिए एक रचनात्मक ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग फ्रेमवर्क का उपयोग करें जिसके लिए इसकी आवश्यकता होती है। यह मेरी राय में एक सुंदर प्रयोगात्मक समाधान है।

आपके द्वारा वर्णित स्थिति सामान्य रूप से संभालना आसान नहीं है। एक समान मामला एक ऐसे व्यवसाय अनुप्रयोग में संस्थाएं होगी जिसके लिए डेटाबेस तक पहुंच की आवश्यकता होती है।


जबकि आप एक इंटरफेस में एक कन्स्ट्रक्टर हस्ताक्षर को परिभाषित नहीं कर सकते हैं, मुझे लगता है कि यह उल्लेखनीय है कि यह एक अमूर्त वर्ग पर विचार करने के लिए एक जगह हो सकती है। सार कक्षाएं एक इंटरफ़ेस के समान तरीके से अनुपूरक (सार) विधि हस्ताक्षर परिभाषित कर सकती हैं, लेकिन कार्यान्वित (ठोस) विधियों और रचनाकारों को भी लागू कर सकती हैं।

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


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

तो आपके पास आपका मुख्य इंटरफ़ेस है जो अब तक ऐसा दिखता है:

public interface IDrawable
{
    void Update();
    void Draw();
}

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

public abstract class MustInitialize<T>
{
    public MustInitialize(T parameters)
    {

    }
}

अब आपको एक नई कक्षा बनाने की आवश्यकता होगी जो पहचानने योग्य इंटरफेस और जरूरी सार तत्व दोनों से विरासत में प्राप्त हो:

public class Drawable : MustInitialize<GraphicsDeviceManager>, IDrawable
{
    GraphicsDeviceManager _graphicsDeviceManager;

    public Drawable(GraphicsDeviceManager graphicsDeviceManager)
        : base (graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }

    public void Update()
    {
        //use _graphicsDeviceManager here to do whatever
    }

    public void Draw()
    {
        //use _graphicsDeviceManager here to do whatever
    }
}

फिर बस ड्रायरेबल का एक उदाहरण बनाएं और आप जाने के लिए अच्छे हैं:

IDrawable drawableService = new Drawable(myGraphicsDeviceManager);

यहां अच्छी बात यह है कि हमने बनाई गई नई ड्रायबल क्लास अभी भी वैसे ही व्यवहार करती है जो हम पहचानने योग्य से अपेक्षा करते हैं।

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


यदि इंटरफेस में रचनाकारों को परिभाषित करना संभव था तो यह बहुत उपयोगी होगा।

यह देखते हुए कि एक इंटरफ़ेस एक अनुबंध है जिसे निर्दिष्ट तरीके से उपयोग किया जाना चाहिए। निम्नलिखित परिदृश्य कुछ परिदृश्यों के लिए एक व्यवहार्य विकल्प हो सकता है:

public interface IFoo {

    /// <summary>
    /// Initialize foo.
    /// </summary>
    /// <remarks>
    /// Classes that implement this interface must invoke this method from
    /// each of their constructors.
    /// </remarks>
    /// <exception cref="InvalidOperationException">
    /// Thrown when instance has already been initialized.
    /// </exception>
    void Initialize(int a);

}

public class ConcreteFoo : IFoo {

    private bool _init = false;

    public int b;

    // Obviously in this case a default value could be used for the
    // constructor argument; using overloads for purpose of example

    public ConcreteFoo() {
        Initialize(42);
    }

    public ConcreteFoo(int a) {
        Initialize(a);
    }

    public void Initialize(int a) {
        if (_init)
            throw new InvalidOperationException();
        _init = true;

        b = a;
    }

}

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

इंटरफ़ेस में एक SetGraphicsDeviceManager (GraphicsDeviceManager gdo) जोड़ें, और इस तरह कार्यान्वयन कक्षाओं को एक तर्क लिखने के लिए मजबूर किया जाएगा जिसके लिए कन्स्ट्रक्टर से कॉल की आवश्यकता होगी।


सामान्य कारखाना दृष्टिकोण अभी भी आदर्श लगता है। आपको पता चलेगा कि फैक्ट्री को पैरामीटर की आवश्यकता है, और ऐसा ही होगा कि उन पैरामीटर को ऑब्जेक्ट के कन्स्ट्रक्टर के साथ तत्काल किया जा रहा है।

नोट, यह सिंटैक्स सत्यापित छद्म कोड है, यहां एक रन-टाइम चेतावनी हो सकती है जो मैं यहां याद कर रहा हूं:

public interface IDrawableFactory
{
    TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
              where TDrawable: class, IDrawable, new();
}

public class DrawableFactory : IDrawableFactory
{
    public TDrawable GetDrawingObject<TDrawable>(GraphicsDeviceManager graphicsDeviceManager) 
                     where TDrawable : class, IDrawable, new()
    {
        return (TDrawable) Activator
                .CreateInstance(typeof(TDrawable), 
                                graphicsDeviceManager);
    }

}

public class Draw : IDrawable
{
 //stub
}

public class Update : IDrawable {
    private readonly GraphicsDeviceManager _graphicsDeviceManager;

    public Update() { throw new NotImplementedException(); }

    public Update(GraphicsDeviceManager graphicsDeviceManager)
    {
        _graphicsDeviceManager = graphicsDeviceManager;
    }
}

public interface IDrawable
{
    //stub
}
public class GraphicsDeviceManager
{
    //stub
}

संभावित उपयोग का एक उदाहरण:

    public void DoSomething()
    {
        var myUpdateObject = GetDrawingObject<Update>(new GraphicsDeviceManager());
        var myDrawObject = GetDrawingObject<Draw>(null);
    }

अनुमोदित, आप केवल कारखाने के माध्यम से निर्माण उदाहरण चाहते हैं कि आप हमेशा एक उचित प्रारंभिक वस्तु की गारंटी दें। शायद AutoFac जैसे निर्भरता इंजेक्शन ढांचे का उपयोग करना समझ में आता है; अपडेट () एक नए ग्राफिक्सडिवाइस मैनेजर ऑब्जेक्ट के लिए आईओसी कंटेनर "पूछ" सकता है।







constructor