2012-10-22 40 views
8

जब भी मैं प्रश्न पढ़ता हूं, या स्थिर विरासत का एक समान विषय, आमतौर पर जवाब यह समर्थित नहीं है (हम जानते हैं), और कारण के रूप में दिया जाता है क्योंकि यह एक खराब डिजाइन है और इसे करने का शायद एक बेहतर तरीका है। मुझे ऐसा करने का बेहतर तरीका मिलना अच्छा लगेगा, इसलिए सभी सुझावों के लिए खुला हूं - यहां मैं जो करने की कोशिश कर रहा हूं।सी # आभासी स्थैतिक तरीकों और स्थैतिक उप वर्गों के लिए वैकल्पिक

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

क्या मैं उपभोक्ताओं के लिए लिखने में सक्षम हो चाहते हैं:

SubStatic.MethodFromSub(); 

और भी

SubStatic.MethodFromBase(); 

मैं मैं भी लिख सकता है पता है:

BaseStatic.MethodFromBase() 

स्पष्ट रूप से लेकिन फिर उपभोक्ताओं है यह जानने के लिए कि कौन सी कक्षाएं लागू करती हैं। मैं विरासत के साथ ऐसा नहीं कर सकता क्योंकि मैं एक स्थिर वर्ग को दूसरे से प्राप्त नहीं कर सकता। तो ऐसा करने का एक बेहतर तरीका क्या है?

अब, मैं जानता हूँ कि मैं उदाहरण के वर्ग के रूप में इन कक्षाओं में हो सकता है, और मैं स्थिर के रूप में सभी तरीकों को परिभाषित कर सकते हैं - कि मुझे व्यवहार मैं ऊपर वर्णित देना लेकिन, अन्य समस्याओं की ओर जाता है अर्थात् होगा:

  1. जब मैं यह कर: SubStatic.MethodFromBase()SubStatic स्थिर निर्माता कहा जाता है नहीं है, क्योंकि विधि (माता-पिता के स्थिर निर्माता कहा जाता है)

  2. स्थिर माता पिता तरीकों में से एक एक और विधि कॉल करने की जरूरत है माता पिता स्थिर कक्षा में चल रहा है जो उप वर्ग ओवरराइड कर सकते हैं, मुझे एक वर्चुअल स्थिर की आवश्यकता है उप वर्ग में विधि। जो मुझे पता है मैं नहीं कर सकता।

तो खराब डिजाइन स्पष्ट रूप से - क्या कोई मुझे इसे फिर से करने में मदद कर सकता है? मुझे पता है कि मैं इंस्टेंस विरासत का उपयोग कर सकता हूं और आभासी तरीकों का उचित उपयोग कर सकता हूं (मैंने इसे इस तरह से काम किया है) लेकिन क्लाइंट कोड को हमेशा एक उदाहरण बनाना होता है (या मुझे लगता है कि कुछ सिंगलटन)।

+1

आप सिंगलटन डिज़ाइन पैटर्न का उपयोग कर सकते हैं जिसमें आप उदाहरण बनाते हैं और उन्हें उपयोगकर्ता को वापस कर देते हैं। क्या इससे आपकी समस्या को हल करने में मदद नहीं मिलेगी? – manman

+0

हां - लेकिन फिर भी मैं एक उदाहरण बना रहा हूं, है ना? इसके अलावा, उपभोक्ताओं को लिखना होगा: SomeClass.Instance.SomeMethod(), है ना? – RBrowning99

+0

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

उत्तर

4

यह आपके उद्देश्य को पूरा कर सकता है, हालांकि मैं निश्चित रूप से कुछ अपवाद हैंडलिंग शामिल करूँगा और दस्तावेज़ीकरण के साथ इसके कार्यान्वयन के साथ इसके कार्यान्वयन के साथ क्यों और कैसे काम करता है।

जब Base के लिए स्थिर कन्स्ट्रक्टर चलाया जाता है (एक बार) वर्तमान में ऐप डोमेन में लोड की गई सभी असेंबली सूचीबद्ध होती हैं, Base से प्राप्त प्रकारों का चयन करते हैं। उन पर इटरेटिंग, हम स्थिर रचनाकार चलाते हैं। यद्यपि यह ध्यान देने योग्य है कि यह अब प्रत्येक कार्यान्वयन के लिए कर्क्टर की गारंटी नहीं देता है, एक बार फिर से चलाने के लिए लॉजिक को उनमें से प्रत्येक में जोड़ा जाना होगा। इसके अलावा, प्रकार के बाद Base के लिए cctor चलाया जा रहा है लोड किए गए हैं कि Base

में तरीकों के लिए कॉल द्वारा आरंभ नहीं किया जा जाएगा आभासी तरीकों अनुकरण करने के लिए, आधार विधि को छिपाने के लिए new कीवर्ड का उपयोग करें।आप इसे (उदाहरण में वर्ग B में की तरह) की घोषणा के वर्ग के नाम के साथ योग्यता

using System; 
using System.Linq; 
using System.Runtime.CompilerServices; 

namespace ConsoleApplication6 
{ 
    public class Base 
    { 
     static Base() 
     { 
      Console.WriteLine("Base cctor"); 

      var thisType = typeof (Base); 
      var loadedTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()); 
      var derivations = loadedTypes.Where(thisType.IsAssignableFrom); 

      foreach(var derivation in derivations) 
      { 
       RuntimeHelpers.RunClassConstructor(derivation.TypeHandle); 
      } 
     } 

     public static void Foo() 
     { 
      Console.WriteLine("Bar"); 
     } 
    } 

    public class A : Base 
    { 
     static A() 
     { 
      Console.WriteLine("A cctor"); 
     } 
    } 

    public class B : Base 
    { 
     static B() 
     { 
      Console.WriteLine("B cctor"); 
     } 

     public new static void Foo() 
     { 
      Console.WriteLine("Bar!!"); 
      Base.Foo(); 
     } 
    } 

    class Program 
    { 
     static void Main() 
     { 
      Console.WriteLine("A:"); 
      A.Foo(); 
      Console.WriteLine(); 
      Console.WriteLine("B:"); 
      B.Foo(); 
      Console.WriteLine(); 
      Console.WriteLine("Base:"); 
      Base.Foo(); 
      Console.ReadLine(); 
     } 
    } 
} 

संपादित करें द्वारा आधार विधि कॉल कर सकते हैं

एक अन्य विकल्प सी # प्रतिमान में CRTP (या CRGP में निहित है) या दिलचस्प आवर्ती टेम्पलेट (सामान्य) पैरामीटर पैटर्न

using System; 
using System.Runtime.CompilerServices; 

namespace ConsoleApplication6 
{ 
    public class Base<T> 
     where T : Base<T> 
    { 
     static Base() 
     { 
      RuntimeHelpers.RunClassConstructor(typeof (T).TypeHandle); 
     } 

     public static void Foo() 
     { 
      Console.WriteLine("Bar"); 
     } 
    } 

    public class Base : Base<Base> 
    { 
    } 

    public class A : Base<A> 
    { 
     static A() 
     { 
      Console.WriteLine("A cctor"); 
     } 
    } 

    public class B : Base<B> 
    { 
     static B() 
     { 
      Console.WriteLine("B cctor"); 
     } 

     public new static void Foo() 
     { 
      Console.WriteLine("Bar!!"); 
      Base<B>.Foo(); 
     } 
    } 

    class Program 
    { 
     static void Main() 
     { 
      Console.WriteLine("A:"); 
      A.Foo(); 
      Console.WriteLine(); 
      Console.WriteLine("B:"); 
      B.Foo(); 
      Console.WriteLine(); 
      Console.WriteLine("Base:"); 
      Base.Foo(); 
      Console.ReadLine(); 
     } 
    } 
} 

इस मामले में, जब हम A पर एक स्थिर विधि कॉल हम वास्तव में यहपर कॉल कर रहे हैंजो Base<B> या Base से अलग है, इसलिए हम वास्तव में निर्धारित कर सकते हैं कि विधि को उचित कैक्टर कहलाया गया था और चलाया गया था।

+0

हम्म - बहुत चालाक लेकिन एक मुद्दा जो मैं देखता हूं वह यह है कि सभी स्थिर उप वर्ग रचनाकारों को बुलाया जाएगा - मेरे पास 100 ऐसे उप-वर्ग हो सकते हैं और यह अपमानजनक होगा। यह दिलचस्प है कि लोग इस काम को बनाने के तरीकों का सुझाव दे रहे हैं कि डिजाइन क्यों खराब है। – RBrowning99

+0

@ RBrowning99 यही कारण है कि मैंने आपसे पूछा कि क्या यह ठीक है अगर सभी व्युत्पन्न कक्षाओं को कैटर कहा जाता है;) – mlorbetske

+0

मैंने आपके प्रश्न को गलत समझा - मैंने सोचा कि आप इस बात पर ध्यान दे रहे थे कि क्या मुझे परवाह है कि माता-पिता या उप-वर्ग कोंक्टर को बुलाया गया था या नहीं। माफ़ कीजिये। – RBrowning99

2

आप जेनिक्स का उपयोग करके इसे प्राप्त कर सकते हैं। उदाहरण के लिए आप इस तरह कुछ उपयोग कर सकते हैं:

public class MainStatic<T> where T : MainStatic<T> 
{ 
    public static void Foo() 
    { 
    } 

    static MainStatic() 
    { 
     RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); 
    } 
} 

public class SubStatic : MainStatic<SubStatic> 
{ 
    public static void Bar() 
    { 
    } 
} 

public class Instance 
{ 
    public void FooBar() 
    { 
     SubStatic.Foo(); 
     SubStatic.Bar(); 
    } 
} 
+0

जब आप 'सबस्टैटिक' पर 'फू' कहते हैं तो 'सबस्टैटिक' पर स्थिर कन्स्ट्रक्टर (सीक्टर) नहीं कहा जाता है। यह ओपी के प्रश्नों में से एक है, जब उसके आधार से एक स्थैतिक सदस्य को बुलाया जाता है तो कैसेक्टर को चलाने के लिए मजबूर किया जाता है। – mlorbetske

+0

दरअसल, मेरी बुराई मैंने यह जांच नहीं की। मैं देखूंगा कि मैं इसे बदल सकता हूं या नहीं। – Guillaume

+0

मेरा उत्तर देखें, आपको बस 'RuntimeHelpers.RunClassConstructor (टाइपोफ़ (टी)। टाइप टाइप) कॉल करने की आवश्यकता है; ' – mlorbetske