2012-12-05 17 views
14

मेरा प्रश्न Is there a reasonable approach to "default" type parameters in C# Generics? से संबंधित है, लेकिन एक आंतरिक जेनेरिक क्लास का उपयोग करना जो दृष्टिकोण नहीं करता है।क्या मैं एक सामान्य वैकल्पिक, एक निश्चित वर्ग के लिए डिफ़ॉल्ट कर सकता हूं?

इस तरह

को देखते हुए कोड:

using System; 

public class FooEventArgs<T> : EventArgs 
{ 
    // ... T properties and a constructor 
} 

public class Foo<T> 
{ 
    public delegate void EventHandler<FooEventArgs>(object sender, FooEventArgs<T> e); 
    public event EventHandler<FooEventArgs<T>> Changed 
} 

और यह इस तरह इस्तेमाल किया जा रहा साथ: के रूप में वहाँ होगा,

public class User 
{ 
    public Foo<int> foo1; 
    public Foo<object> foo2; 

    public User() 
    { 
     foo1 = new Foo<int>(); 
     foo2 = new Foo<object>(); 
     foo1.Changed += foo1_Changed; 
     foo2.Changed += foo2_Changed; 
    } 

    protected void foo1_Changed(object sender, FooEventArgs<int> e) { ... } 
    protected void foo2_Changed(object sender, FooEventArgs<object> e) { ... } 
} 

ठीक है, मैं नहीं बल्कि यह चाहते हैं मैं सामान्य वैकल्पिक हो सकता था अगर कई मामलों में जहां मुझे नहीं पता कि किस प्रकार का कुछ आ जाएगा। (डेटा बाहरी सिस्टम से आ रहा है जिसमें इसके अपने चर प्रकार हैं, जिन्हें बाद में .NET प्रकार में परिवर्तित किया जाता है, लेकिन मैं परिस्थितियों में भाग लेता हूं, उदाहरण के लिए , एक दूरस्थ डेटा प्रकार दो .NET प्रकारों में से एक में बदल सकता है, या जहां यह "कोई भी" है टाइप-इस प्रकार object कि मामले के लिए केवल वास्तविक जवाब होगा)

समाधान जो तुरंत घटित हुआ (उपवर्गीकरण था यह भी सवाल पहले से जुड़ा हुआ में प्राथमिक सुझाव) था:।

public class Foo : Foo<object> 
{ 
    public Foo(...) : base(...) { } 
} 

public class FooEventArgs : FooEventArgs<object> 
{ 
    public Foo(...) : base(...) { } 
} 

मैं तो इस तरह उपयोग करना चाहते हैं:

public class User 
{ 
    public Foo foo3; 

    public User() 
    { 
     foo3 = new Foo(); 
     foo3.Changed += foo3_Changed; 
    } 

    protected void foo3_Changed(object sender, FooEventArgs e) { ... } 
} 

समस्या यह है कि यह स्वाभाविक रूप से foo3_Changed स्वीकार करने FooEventArgs साथ काम नहीं करेंगे है; इसे FooEventArgs<object> की आवश्यकता है, जैसा कि Foo.Changed ईवेंट पास हो जाएगा (क्योंकि मान Foo<object> से आएगा)।

Foo.cs(3,1415926): error CS0123: No overload for 'foo3_Changed' matches delegate 'FooLibrary.Foo<object>.EventHandler<FooLibrary.FooEventArgs<object>>' 

क्या इस बारे में कुछ भी मैं कर सकता हूं, कक्षा के अधिकांश डुप्लिकेट करने से कम?

मैंने एक और चीज की कोशिश की: FooEventArgs<object> से FooEventArgs में परिवर्तित करने के लिए एक निहित ऑपरेटर। ,

EditBuffer.cs(13,37): error CS0553: 'FooLibrary.FooEventArgs.implicit operator FooLibrary.FooEventArgs(FooLibrary.FooEventArgs<object>)': user-defined conversions to or from a base class are not allowed 

तो फिर, एक बार फिर से, वहाँ कुछ भी मैं इस बारे में क्या कर सकते हैं:

public static implicit operator FooEventArgs(FooEventArgs<object> e) 
    { 
     return new FooEventArgs(...); 
    } 

यह, दुर्भाग्य से, काम करने के लिए, नहीं लगता है कि मैं पर क्यों काफी स्पष्ट नहीं कर रहा हूँ या क्या मैं यह सोचने में सही हूं कि यह कठिन भाग्य है और मुझे केवल FooEventArgs<object> का उपयोग करके सामग्री होना होगा (और फिर मुझे लगता है कि मैं Foo<object> का उपयोग कर सकता हूं)?

+0

[संबंधित] (https://github.com/dotnet/csharplang/issues/253) – Shimmy

उत्तर

20

मुझे नहीं लगता कि आप ईमानदार होने के लिए इसके बारे में बहुत कुछ कर सकते हैं। आप Foo दोगुना सामान्य बना सकता है:

public class Foo<TData, TArgs> where TArgs : FooEventArgs<TData> 
{ 
    public delegate void EventHandler<TArgs>(object sender, TArgs e); 
    public event EventHandler<TArgs> Changed; 
} 

तो फिर तुम लिख सकते हैं:

public class Foo : Foo<object, FooEventArgs> 

... लेकिन यह वास्तव में बातें बहुत कम लाभ के लिए बहुत जटिल बना रहा है।

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

आपके अंतर्निहित रूपांतरण में काम नहीं करने का कारण जेनिक्स के साथ कुछ भी नहीं है, वैसे ही - त्रुटि संदेश कहता है, आप एक रूपांतरण (निहित या स्पष्ट) घोषित नहीं कर सकते जो विरासत पदानुक्रम ऊपर या नीचे जाता है । सी # स्पेक सेक्शन 6.4.1:

सी # केवल कुछ उपयोगकर्ता परिभाषित रूपांतरणों को घोषित करने की अनुमति देता है। विशेष रूप से, पहले से मौजूद मौजूदा निहित या स्पष्ट रूपांतरण को फिर से परिभाषित करना संभव नहीं है।

इंटरफेस के साथ आम तौर पर (अधिक जानकारी के लिए कि अनुभाग देखें।)


एक तरफ ध्यान दें के रूप में, मैं इसे और अधिक आम विरासत जेनरिक के लिए अन्य इसका उल्टा उपयोग करने के लिए मिल जाए,:

public interface IFoo 
{ 
    // Members which don't depend on the type parameter 
} 

public interface IFoo<T> : IFoo 
{ 
    // Members which all use T 
} 

इस तरह कोड चीजों की जेनरिक पक्ष के बारे में चिंता करता है, तो वे जानना चाहते हैं T की जरूरत नहीं है के बिना सिर्फ एक IFoo प्राप्त कर सकते हैं।

दुर्भाग्य से, यह आपके विशिष्ट मामले में आपकी सहायता नहीं करता है।

+0

इंटरफ़ेस विरासत के साथ काम कर के विपरीत ढंग पर एक दिलचस्प अंतर्दृष्टि है कि । लेकिन जैसा कि आप देखते हैं, वह इस मामले में नहीं करेगा। धन्यवाद। –

+0

@ChrisMorgan: हाँ - मैं केवल इसे एक साइड पदार्थ के रूप में लाया। मैंने किसी अन्य विचार के साथ उत्तर की शुरुआत संपादित की है, लेकिन यह वास्तव में एक * जैसा नहीं है। –

+0

नहीं, मुझे यह पसंद नहीं है ... ठीक है, तो मुझे लगता है कि यह स्पष्ट है। –

8

एक और दिलचस्प बात जो मैंने अभी पाया है वह है कि आप एक ही नाम के साथ सामान्य वर्ग बना सकते हैं लेकिन विभिन्न हस्ताक्षर।

new Foo<string>(); 
new Foo<int,double>(); 
new Foo<string,int>(); 

मैं बस सोचा था कि यह दिलचस्प है कि दोनों वर्गों में एक ही नाम वे कर सकते हैं होने के बावजूद सह-अस्तित्व क्योंकि वे विभिन्न हस्ताक्षर है था:

class Foo<T> { 

} 

class Foo<T,T> { 

} 

तो आप या तो उनमें से एक की तरह इस प्रकार कह सकते हैं।

मैं इस है लगता है कि कैसे टपल वर्ग काम करता है

संबंधित मुद्दे