2011-10-14 20 views
5

मुझे कुछ सामान्य तरीकों के साथ एक इंटरफ़ेस मिला है, और मैं कक्षा के उदाहरण को स्वीकार करने के लिए ओवरलोड के साथ एक विधि को कार्यान्वित करना चाहता था, या उसके पीके मान (जो या तो एक इंट या GUID है लेकिन भिन्न होता है)।विधि ओवरलोड पर सामान्य बाधा

मैं इन उदाहरणों के समान तरीकों को जोड़ा गया:

void DoSomething<TKey>(TKey key) where TKey: struct; 
    void DoSomething<TModel>(TModel model) where TModel : class; 

इन के दूसरे पर 'DoSomething' विधि नाम हाइलाइट किया गया है, और त्रुटि

प्रकार 'ISomeStuff' पहले से ही परिभाषित करता है के समान पैरामीटर प्रकार वाले 'DoSomething' नामक एक सदस्य।

मैं इस बात से हैरान हूं क्योंकि मैंने पैरामीटर द्वारा स्पष्ट रूप से विभिन्न प्रकार के रूप में परिभाषित किया है: एक वर्ग और दूसरा एक संरचना है।

हस्ताक्षर अलग करने के लिए पर्याप्त क्यों नहीं है?

+1

संभावित डुप्लिकेट [जेनेरिक की कमी है, जहां टी: struct और जहां टी: वर्ग] (http://stackoverflow.com/questions/2974519/generic-constraints-where-t-struct-and-where- टी वर्ग)। एरिक लिपर्ट का लेख भी देखें [यहां] (http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-ignature.aspx)। –

+0

@ फ़्रेडरिक: मुझे यह कैसे याद आया !!! –

+0

स्पष्ट रूप से साइडबार में "संबंधित" फलक इसे नहीं चुनता था, इसलिए यह सामान्य से अधिक कठिन हो सकता है;) –

उत्तर

3

जॉन स्कीट सब कुछ करने के लिए एक जवाब है: click me

बोली:

घोषणाओं केवल सामान्य बाधाओं में मतभेद है, और बाधाओं हस्ताक्षर

+1

भविष्य के पाठकों के लिए अद्यतन लिंक (जॉन ने अपने व्यक्तिगत ब्लॉग पर एक ही लेख पोस्ट किया है): http://codeblog.jonskeet.uk/2010/10/28/overloading-and-generic-constraints/ –

5

है का हिस्सा नहीं हैं ऐसा करने के लिए संभव है, आपको C12+

से enable_if जैसे कुछ बनाने की आवश्यकता है

इसे ओवरलोड के बीच अंतर करने के लिए किसी भी डिज्जेंट where का उपयोग करने के लिए विस्तारित किया जा सकता है।

public static void Z1<T>(T t) // where T : class 
{ 
    Func(t); //error there 
} 

public static void Z2<T>(T t) where T : class 
{ 
    Func(t); //ok 
} 

संपादित लेकिन उस मामले में उपयोग dynamic की संभावना है इस सीमा के आसपास काम करने के लिए:

public static void Z1<T>(T t) 
{ 
    Func((dynamic)t); //if `T == int` it will call "struct" version 
} 

केवल दोष यह केवल दोष यह है कि यह एक और सामान्य विधि के अंदर इस्तेमाल किया जा नहीं कर सकते है Dictionary<,> अनुक्रमणिका पर कॉल के समान रन टाइम लागत है।

+0

मुझे यह पसंद है जवाब। –

1

एक सामान्य रूप से है कि क्या यह एक वर्ग बाधा या एक struct बाधा है की परवाह किए बिना एक सदस्य आह्वान, और यह एक उपयुक्त बाधा के साथ एक विधि आह्वान करने के लिए चाहता है, एक किसी भी प्रकार T पर कार्रवाई करने के लिए, साथ एक अंतरफलक IThingUser<T> परिभाषित कर सकते हैं एक वर्ग के साथ जो इसे मूल्य प्रकारों के लिए लागू करता है और दूसरा जो इसे वर्ग प्रकारों के लिए लागू करता है। एक स्थिर वर्ग ThingUsers<T> एक स्थिर क्षेत्र TheUser प्रकार IThingUser<T> के साथ, और उपरोक्त वर्गों में से किसी एक के उदाहरण के साथ उस क्षेत्र को पॉप्युलेट करें, और फिर ThingUsers<T>.theUser किसी भी प्रकार के T पर कार्य करने में सक्षम हो जाएगा।

public static class GenTest93 
{ 
    public interface IThingUser<T> { void ActOnThing(T it); } 
    class StructUser<T> : IThingUser<T>, IThingUser<Nullable<T>> where T : struct 
    { 
     void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Struct {0}", typeof(T)); } 
     void IThingUser<Nullable<T>>.ActOnThing(T? it) { System.Diagnostics.Debug.Print("Struct? {0}", typeof(T)); } 
    } 
    class ClassUser<T> : IThingUser<T> where T : class 
    { 
     void IThingUser<T>.ActOnThing(T it) { System.Diagnostics.Debug.Print("Class {0}", typeof(T)); } 
    } 
    static class ThingUsers<T> 
    { 
     class DefaultUser : IThingUser<T> 
     { 
      public void ActOnThing(T it) 
      { 
       Type t = typeof(T); 
       if (t.IsClass) 
        t = typeof(ClassUser<>).MakeGenericType(typeof(T)); 
       else 
       { 
        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) 
         t = t.GetGenericArguments()[0]; 
        t = typeof(StructUser<>).MakeGenericType(t); 
       } 
       TheUser = (IThingUser<T>)Activator.CreateInstance(t); 
       TheUser.ActOnThing(it); 
      } 
     } 
     static IThingUser<T> TheUser = new DefaultUser(); 
     public static void ActOnThing(T it) {TheUser.ActOnThing(it);} 
    } 
    public static void ActOnThing<T>(T it) { ThingUsers<T>.ActOnThing(it); } 
    public static void Test() 
    { 
     int? foo = 3; 
     ActOnThing(foo); 
     ActOnThing(5); 
     ActOnThing("George"); 
    } 
} 

यह अगर संकलक नहीं जानता है कि T संतुष्ट आवश्यक बाधा, लेकिन ऐसा नहीं भी मुश्किल है StructUser<T> या ClassUser<T> का एक उदाहरण बनाने के लिए प्रतिबिंब का उपयोग करने के लिए आवश्यक है।पहली बार ActOnThing<T>() का उपयोग किसी विशेष T, ThingUsers<T>.TheUser will be set to an instance which can be used directly for any future calls to ActOnThing() के लिए किया जाता है, इसलिए प्रदर्शन बहुत अच्छा होना चाहिए।

ध्यान दें कि यदि एक Nullable<T> को देखते हुए विधि एक StructUser<T> बनाता है और IThingUser<Nullable<T>> करने के लिए इसे डाले, बल्कि एक sometype<Nullable<T>> बनाने के लिए कोशिश कर रहा से, नल प्रकार के बाद से खुद को किसी भी बाधा को संतुष्ट नहीं है।

1

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

void Foo(object a) { } // reference type 
void Foo<T>(T? a) where T : struct { } // nullable 
void Foo(ValueType a) { } // value type 
की
संबंधित मुद्दे