2011-01-22 11 views
22

में बेनामी आंतरिक वर्ग सी # और विकेट की मेरी समझ को गहरा बनाने के लिए मैं सी # विकेट कार्यान्वयन लिखने की प्रक्रिया में हूं। जिन मुद्दों में हम चल रहे हैं उनमें से एक यह है कि विकेट अज्ञात आंतरिक कक्षाओं का भारी उपयोग करता है, और सी # में कोई अज्ञात भीतरी कक्षा नहीं है।सी #

तो, उदाहरण के लिए, विकेट में, आप एक लिंक इस तरह परिभाषित करते हैं:

Link link = new Link("id") { 
    @Override 
    void onClick() { 
     setResponsePage(...); 
    } 
}; 

के बाद से लिंक एक अमूर्त वर्ग है, यह एक onClick पद्धति को लागू करने के implementor बाध्य करती है।

हालांकि, सी # में, क्योंकि कोई अज्ञात भीतरी कक्षा नहीं है, ऐसा करने का कोई तरीका नहीं है। एक विकल्प के रूप में, आप इस तरह की घटनाओं का उपयोग कर सकते हैं:

var link = new Link("id"); 
link.Click += (sender, eventArgs) => setResponsePage(...); 

बेशक, इसके साथ कुछ कमियां हैं। सबसे पहले, कई क्लिक हैंडलर हो सकते हैं, जो ठंडा नहीं हो सकता है। यह एक क्लिक हैंडलर जोड़ने के लिए कार्यान्वयन को मजबूर नहीं करता है।

एक अन्य विकल्प सिर्फ इस तरह एक बंद संपत्ति के लिए हो सकता है:

var link = new Link("id"); 
link.Click =() => setResponsePage(...); 

यह कई संचालकों होने की समस्या का हल है, लेकिन अभी हैंडलर जोड़ने के implementor मजबूर नहीं करता है।

तो, मेरा सवाल यह है कि आप इस तरह कुछ बेवकूफ सी # में कैसे अनुकरण करते हैं?

+1

मुझे आपके द्वारा दिए गए उदाहरण में अज्ञात आंतरिक वर्ग नहीं दिख रहा है। यदि आप अपने अमूर्त वर्ग के कार्यान्वयनकर्ताओं को हमेशा कुछ तरीकों को लागू करने के लिए चाहते हैं तो आप कक्षा में एक अमूर्त विधि बना सकते हैं या इसे एक इंटरफेस लागू कर सकते हैं। – tenor

+2

@tenor, एक इनलाइन अज्ञात वर्ग परिभाषित किया गया है जो 'लिंक' से विरासत में मिलता है और 'onClick' विधि को ओवरराइड करता है। जावा के विपरीत, सी # किसी दिए गए उपयोगकर्ता प्रकार से प्राप्त करने के लिए अज्ञात कक्षाओं का समर्थन नहीं करता है। –

+0

@ डारिन डिमिट्रोव, यह इंगित करने के लिए धन्यवाद। मैं एक सच्चे "आंतरिक/घोंसला" वर्ग की तलाश में था। प्रदान किया गया उदाहरण एक अज्ञात वर्ग की तरह दिखता है जो कम से कम सी # लिंगो में मौजूदा वर्ग से निकला है। – tenor

उत्तर

17

आप प्रतिनिधि को लिंक क्लास के निर्माता का हिस्सा बन सकते हैं। इस तरह उपयोगकर्ता को इसे जोड़ना होगा।

public class Link 
{ 
    public Link(string id, Action handleStuff) 
    { 
     ... 
    } 

} 

तो फिर तुम एक उदाहरण इस तरह से बनाने के लिए:

var link = new Link("id",() => do stuff); 
+0

हाँ, यह नामित पैरामीटर के साथ भी अच्छी तरह से काम करेगा। – cdmckay

1

मैं @ meatthew के अच्छे जवाब से पहले इस शुरू कर दिया - मैं लगभग ठीक छोड़कर भी ऐसा ही होगा - सिवाय इसके कि मैं एक सार आधार वर्ग के साथ शुरू होगा - ताकि अगर आप किसी अज्ञात कार्यान्वयन के मार्ग पर नहीं जाना चाहते हैं तो आप भी ऐसा करने के लिए स्वतंत्र होंगे।

public abstract class LinkBase 
{ 
    public abstract string Name { get; } 
    protected abstract void OnClick(object sender, EventArgs eventArgs); 
    //... 
} 

public class Link : LinkBase 
{ 
    public Link(string name, Action<object, EventArgs> onClick) 
    { 
     _name = Name; 
     _onClick = onClick; 
    } 

    public override string Name 
    { 
     get { return _name; } 
    } 

    protected override void OnClick(object sender, EventArgs eventArgs) 
    { 
     if (_onClick != null) 
     { 
      _onClick(sender, eventArgs); 
     } 
    } 

    private readonly string _name; 
    private readonly Action<object, EventArgs> _onClick; 

} 
3

यह मैं क्या कर सकता है:

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

यहां कुछ उदाहरण कोड है:

class Program 
{ 
    static void Main(string[] args) 
    { 

     Link link = LinkFactory.GetLink("id",() => 
     // This would be your onClick method. 
     { 
       // SetResponsePage(...); 
       Console.WriteLine("Clicked"); 
       Console.ReadLine(); 
     }); 
     link.FireOnClick(); 
    } 
    public static class LinkFactory 
    { 
     private class DerivedLink : Link 
     { 
      internal DerivedLink(String id, Action action) 
      { 
       this.ID = id; 
       this.OnClick = action; 
      } 
     } 
     public static Link GetLink(String id, Action onClick) 
     { 
       return new DerivedLink(id, onClick); 
     } 
    } 
    public abstract class Link 
    { 
     public void FireOnClick() 
     { 
      OnClick(); 
     } 
     public String ID 
     { 
      get; 
      set; 
     } 
     public Action OnClick 
     { 
      get; 
      set; 
     } 
    } 
} 

संपादित करें: वास्तव में, यह करने के लिए एक छोटे से करीब तुम क्या चाहते हो सकता है:

Link link = new Link.Builder 
{ 
    OnClick =() => 
    { 
     // SetResponsePage(...); 
    }, 
    OnFoo =() => 
    { 
     // Foo! 
    } 
}.Build("id"); 

सौंदर्य यह एक init ब्लॉक का उपयोग करता है, आपको लिंक क्लास के भीतर कार्यों के कई वैकल्पिक कार्यान्वयन की घोषणा करने की इजाजत देता है।

यहां प्रासंगिक लिंक क्लास (सीलबंद बिल्डर आंतरिक कक्षा के साथ) है।

public class Link 
{ 
    public sealed class Builder 
    { 
     public Action OnClick; 
     public Action OnFoo; 
     public Link Build(String ID) 
     { 
      Link link = new Link(ID); 
      link.OnClick = this.OnClick; 
      link.OnFoo = this.OnFoo; 
      return link; 
     } 
    } 
    public Action OnClick; 
    public Action OnFoo; 
    public String ID 
    { 
     get; 
     set; 
    } 
    private Link(String ID) 
    { 
     this.ID = ID; 
    } 
} 

यह आप के लिए क्या देख रहे हैं के करीब है, लेकिन मुझे लगता है कि हम इसे वैकल्पिक नामित तर्क के साथ एक कदम आगे ले जा सकते हैं, एक सी # 4.0 सुविधा। चलिए वैकल्पिक नामित तर्कों के साथ लिंक की घोषणा की उदाहरण देखें:

Link link = Link.Builder.Build("id", 
    OnClick:() => 
    { 
     // SetResponsePage(...); 
     Console.WriteLine("Click!"); 
    }, 
    OnFoo:() => 
    { 
     Console.WriteLine("Foo!"); 
     Console.ReadLine(); 
    } 
); 

यह अच्छा क्यों है? की नई लिंक वर्ग पर नजर डालते हैं:

public class Link 
{ 
    public static class Builder 
    { 
     private static Action DefaultAction =() => Console.WriteLine("Action not set."); 
     public static Link Build(String ID, Action OnClick = null, Action OnFoo = null, Action OnBar = null) 
     { 
      return new Link(ID, OnClick == null ? DefaultAction : OnClick, OnFoo == null ? DefaultAction : OnFoo, OnBar == null ? DefaultAction : OnBar); 
     } 
    } 
    public Action OnClick; 
    public Action OnFoo; 
    public Action OnBar; 
    public String ID 
    { 
     get; 
     set; 
    } 
    private Link(String ID, Action Click, Action Foo, Action Bar) 
    { 
     this.ID = ID; 
     this.OnClick = Click; 
     this.OnFoo = Foo; 
     this.OnBar = Bar; 
    } 
} 

स्थिर वर्ग बिल्डर के अंदर, वहाँ एक कारखाने विधि बिल्ड कि 1 आवश्यक पैरामीटर (आईडी) और 3 वैकल्पिक पैरामीटर, OnClick, OnFoo और OnBar में ले जाता है। अगर उन्हें असाइन नहीं किया गया है, तो फैक्ट्री विधि उन्हें एक डिफ़ॉल्ट कार्यान्वयन देती है।

तो लिंक के लिए आपके कन्स्ट्रक्टर के पैरामीटर तर्कों में, आपको केवल उन्हीं विधियों को लागू करने की आवश्यकता है, अन्यथा वे डिफ़ॉल्ट कार्रवाई का उपयोग करेंगे, जो कुछ भी नहीं हो सकता है।

हालांकि, अंतिम उदाहरण में दोष, सारणी सार सारणी नहीं है। लेकिन इसे लिंक क्लास के दायरे से बाहर नहीं किया जा सकता है, क्योंकि इसका कन्स्ट्रक्टर निजी है (लिंक को चालू करने के लिए बिल्डर क्लास के उपयोग को मजबूर करना)।

आप वैकल्पिक पैरामीटर को सीधे एक कारखाने की आवश्यकता से परहेज करते हुए सीधे लिंक के कन्स्ट्रक्टर में स्थानांतरित कर सकते हैं।

+0

यह उत्तर 3 साल पहले है, लेकिन मैंने सोचा कि मैं आपके अंतिम उत्तर में उल्लेख करता हूं कि आप रिटर्न लाइन को थोड़ा सा छोटा कर सकते हैं: नया लिंक (आईडी, ऑनक्लिक ?? डिफॉल्टएक्शन, ऑनफू ?? डिफॉल्टएक्शन, ऑनबार ?? डिफ़ॉल्ट क्रिया); –