2010-08-31 12 views
36

क्या यह समर्थित नहीं है, क्या यह समर्थित है लेकिन मुझे कुछ चालें करनी हैं?गैर-जेनेरिक वर्ग में सामान्य कन्स्ट्रक्टर समर्थित है?

उदाहरण:

class Foo 
{ 
    public Foo<T1,T2>(Func<T1,T2> f1,Func<T2,T1> f2) 
    { 
    ... 
    } 
} 

जेनरिक केवल निर्माता में किया जाता है, कोई क्षेत्र/संपत्ति उन पर निर्भर है, मैं यह (जेनरिक) का उपयोग F1 और F2 के लिए प्रकार सहसंबंध लागू करने के लिए।

टिप्पणी: मुझे वर्कअराउंड - स्थैतिक विधि बनाएं, लेकिन फिर भी मुझे उत्सुकता है कि मुझे सीधा दृष्टिकोण के साथ समस्या क्यों है।

उत्तर

58

नहीं, जेनेरिक रचनाकार या तो सामान्य या गैर-जेनेरिक वर्गों में समर्थित नहीं हैं। इसी प्रकार सामान्य घटनाएं, गुण और अंतिमकरण समर्थित नहीं हैं।

कभी-कभी मैं सहमत हूं कि यह आसान होगा - लेकिन वाक्यविन्यास बहुत भयानक लगेगा। उदाहरण के लिए, मान लीजिए आप था:

public class Foo<T> {} 

public class Foo 
{ 
    public Foo<T>() {} 
} 

new Foo<string>() 

क्या करना होगा? गैर-जेनेरिक वर्ग, या जेनेरिक वर्ग के सामान्य कन्स्ट्रक्टर के जेनेरिक कन्स्ट्रक्टर को कॉल करें? आप किसी भी तरह उन दोनों के बीच अंतर करने के लिए होगा, और यह गंदा :(

इसी तरह, एक सामान्य वर्ग में एक सामान्य निर्माता पर विचार किया जाएगा:

public class Foo<TClass> 
{ 
    public Foo<TConstructor>() {} 
} 

आप निर्माता कैसे कहेंगे उम्मीद है कि हम सभी कर सकते हैं सहमत हैं कि:

new Foo<string><int>() 

सुंदर घृणित है ...

तो हाँ, शब्दार्थ यह कभी कभी उपयोगी होगा - लेकिन जिसके परिणामस्वरूप कुरूपता कि counterbalances, दुर्भाग्य से।

+0

आप एक ही नाम के साथ एक सामान्य और गैर-जेनेरिक वर्ग की अनुमति नहीं देकर समान नाम वाली कक्षा की समस्या के आसपास हो सकते हैं (गंभीरता से, सी # यह अनुमति देता है?)। सामान्य वर्ग के सामान्य निर्माता के लिए, मुझे नहीं लगता कि यह बहुत ही घृणित है - यह केवल उच्च आदेश सामान्यता है। –

+2

एक टिप्पणी - सामान्य वर्ग में कन्स्ट्रक्टर सामान्य है, क्योंकि कक्षा सामान्य है। हालांकि (आपका उत्तर लेना) यह ** अतिरिक्त ** जेनेरिक तर्क निर्दिष्ट नहीं कर सकता है। बहुत अच्छे उदाहरणों के लिए धन्यवाद! – greenoldman

+1

@ पीटर: नहीं, समान नामित वर्ग समस्या कोई समस्या नहीं है, क्योंकि यद्यपि आप * टाइप * एआरटी टाइप करके कक्षाओं को "ओवरलोड" कर सकते हैं, वहां कोई अस्पष्टता नहीं है। इसके उदाहरण के लिए 'टुपल' देखें। –

15

जेनेरिक निर्माताओं समर्थित नहीं हैं, लेकिन आप इस के आसपास बस एक सामान्य, static विधि है कि रिटर्न को परिभाषित करते हुए प्राप्त कर सकते हैं एक नया Foo:

class Foo 
{ 
    public static Foo CreateFromFuncs<T1,T2>(Func<T1,T2> f1,Func<T2,T1> f2) 
    { 
    ... 
    } 
} 

जो इस तरह प्रयोग किया जाता है:

// create generic dependencies 
var func1 = new Func<byte, string>(...); 
var func2 = new Func<string, byte>(...); 

// create nongeneric Foo from dependencies 
Foo myFoo = Foo.CreateFromFuncs<byte, string>(func1, func2); 
0

यहां एक व्यावहारिक उदाहरण है कि आप अतिरिक्त कन्स्ट्रक्टर प्रकार पैरामीटर और वर्कअराउंड कैसे प्राप्त करना चाहते हैं।

मैं IDisposable के लिए एक सरल RefCounted आवरण लागू करने के लिए जा रहा हूँ:

public class RefCounted<T> where T : IDisposable 
{ 
    public RefCounted(T value) 
    { 
     innerValue = value; 
     refCount = 1; 
    } 

    public void AddRef() 
    { 
     Interlocked.Increment(ref refCount); 
    } 

    public void Dispose() 
    { 
     if(InterlockedDecrement(ref refCount)<=0) 
      innerValue.Dispose(); 
    } 

    private int refCount; 
    private readonly innerValue; 
} 

यह ठीक हो रहा है। लेकिन जल्दी या बाद में आप RefCounted<Control> से RefCounted<Button> पर डालना चाहते हैं, जबकि ऑब्जेक्ट रेफरेंस गिनती दोनों रखें, यानी केवल तभी जब दोनों उदाहरण अंतर्निहित ऑब्जेक्ट को निपटाने के लिए निपटाए जा रहे हों।

सबसे अच्छा तरीका है अगर आप लिख सकते हैं (जैसे सी ++ लोग क्या कर सकते हैं)

public RefCounted(RefCounted<U> other) 
{ 
    ...whatever... 
} 

लेकिन सी # इस अनुमति नहीं देता है। तो समाधान कुछ संकेतों का उपयोग किया जाता है।

private readonly Func<T> valueProvider; 
private readonly Action disposer; 

private RefCounted(Func<T> value_provider, Action disposer) 
{ 
    this.valueProvider = value_provider; 
    this.disposer = disposer; 
} 

public RefCounted(T value) : this(() => value, value.Dispose) 
{ 
} 

public RefCounted<U> Cast<U>() where U : T 
{ 
    AddRef(); 
    return new RefCounted<U>(() => (U)(valueProvider()),this.Dispose); 
} 

public void Dispose(){ 
    if(InterlockedDecrement(ref refCount)<=0) 
     disposer(); 
} 

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

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