2013-03-25 7 views
8

मैं कुछ समझने में कोई परेशानी हो रहा है यही कारण है कि निम्नलिखित स्निपेट मुझे एक त्रुटिजेनेरिक बाधा अपेक्षा के अनुरूप काम नहीं करता है

public void SomeMethod<T>(T arg) where T : MyInterface 
{ 
    MyInterface e = arg; 
} 

लेकिन यह एक है, जो मैं सामान्य की वजह से काम करने के लिए उम्मीद करेंगे नहीं देता प्रकार बाधा

private readonly IList<Action<MyInterface>> myActionList = new List<Action<MyInterface>>(); 

public IDisposable Subscribe<T>(Action<T> callback) where T: MyInterface 
{ 
    myActionList.Add(callback); // doesn't compile 
    return null 
} 

इस त्रुटि

cannot convert from 'System.Action<T>' to 'System.Action<MyInterface>' 

मैं VS2012 SP1 और .NET 4.5 का उपयोग कर रहा देता है।

क्या कोई यह समझा सकता है कि बाधा इसे संकलित करने की अनुमति क्यों नहीं देती है?

public void SomeMethod(MyInterface arg) 
{ 
    MyInterface e = arg; 
} 

private readonly IList<Action<MyInterface>> myActionList = new IList<Action<MyInterface>>(); 

public IDisposable Subscribe(Action<MyInterface> callback) 
{ 
    myActionList.Add(callback); // does compile 
    return null 
} 

काम करते हैं और संकलन और व्यावहारिक रूप से क्या आप अभी उपयोग कर के रूप में ही है होगा:

+1

आपकी सूची क्यों पढ़ी जाती है, और क्यों 'नया IList'? क्या यह वास्तविक घोषणा है? –

+1

कक्षाएं और प्रतिनिधि एक ही बात नहीं हैं। 'System.Action ' 'MyInterface' प्रकार के एक पैरामीटर के साथ एक फ़ंक्शन का प्रतिनिधित्व करता है, जबकि 'System.Action ' प्रकार 'टी: MyInterface' के पैरामीटर वाले विधि का प्रतिनिधित्व करता है। फ़ंक्शन हस्ताक्षर संगत नहीं हैं, यह रिलीज़ नहीं किया जाता है कि 'टी'' MyInterface' का व्युत्पन्न है, हस्ताक्षर केवल तभी संगत होगा यदि 'टी' बिल्कुल 'MyInterface' था। –

+0

@ पाओलो टेडेस्को माफी, यह किसी अन्य कोड से पुनर्निर्मित और सरलीकृत है। कॉपी/पेस्ट त्रुटि –

उत्तर

3

कक्षाएं और प्रतिनिधि एक ही बात नहीं हैं। System.Action<MyInterface>MyInterface के एक पैरामीटर के साथ एक फ़ंक्शन का प्रतिनिधित्व करता है जबकि System.Action<T>T : MyInterface प्रकार के पैरामीटर के साथ एक विधि का प्रतिनिधित्व करता है। फ़ंक्शन हस्ताक्षर संगत नहीं हैं, यह रिलीज़ नहीं है कि TMyInterface का व्युत्पन्न है, हस्ताक्षर केवल तभी संगत होगा यदि T बिल्कुल MyInterface था।

0

T वैसे भी एक निश्चित इंटरफ़ेस तक ही सीमित है, तो आप सिर्फ अपनी जगह में है कि इंटरफ़ेस का उपयोग कर सकते हैं।

जेनिक्स उपयोगी हैं यदि आप उसी ऑपरेशन को रिजर्डलेस करना चाहते हैं, तो यदि आप जेनरिक्स के उद्देश्य को हराकर कुछ इंटरफेस को टाइप करते हैं और शायद इसके बजाय उस इंटरफ़ेस का उपयोग करना चाहिए।

+0

मुझे लगता है कि वह उस विशिष्ट इंटरफ़ेस के उपप्रकारों को पारित करने में सक्षम होना चाहता है .. उदाहरण के लिए। MyOtherInterface: MyInterface –

+0

@Roger जो सच हो सकता है, मुझे ओपी से यह नहीं मिला लेकिन मैं मानता हूं कि यह एक ऐसा दृष्टिकोण है जिसे मैंने नहीं माना था। उदाहरण के साथ स्पष्टीकरण के लिए – Bazzz

4

यह एक contravariance मुद्दा है - एक Action<MyInterface>, एक तर्क के रूप में किसी भी MyInterface उदाहरण लेने के लिए सक्षम होना चाहिए लेकिन आप स्टोर करने के लिए कोशिश कर रहे हैं एक Action<T> जहां TMyInterface के कुछ उप-प्रकार है, जो सुरक्षित नहीं है।

उदाहरण के लिए यदि आप था:

public class SomeImpl : MyInterface { } 
public class SomeOtherImpl : MyInterface { } 
List<Action<MyInterface>> list; 

list.Add(new Action<SomeImpl>(i => { })); 
ActionMyInterface act = list[0]; 
act(new SomeOtherImpl()); 

आप केवल कुछ Action<U> के लिए एक Action<T> असाइन कर सकते हैं, तो प्रकार T प्रकार U से 'छोटे' है। उदाहरण के लिए

Action<string> act = new Action<object>(o => { }); 

सुरक्षित है क्योंकि स्ट्रिंग तर्क हमेशा मान्य होता है जहां ऑब्जेक्ट तर्क होता है।

+0

+1। – nawfal

1

where T: MyInterface बाधा का अर्थ है "किसी भी वर्ग या struct जो MyInterface लागू करता है की किसी भी उदाहरण"।

तो तुम क्या करने की कोशिश कर रहे हैं इस के रूप में सरल किया जा सकता:

Action<IList> listAction = null; 
Action<IEnumerable> enumAction = listAction; 

कौन सा, काम करने के लिए माना जाता नहीं है, जबकि अभी भी IList : IEnumerable।अधिक जानकारी यहां पाया जा सकता है:

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx http://msdn.microsoft.com/en-us/library/dd799517.aspx

तो तुम वास्तव में सामान्य और न कि केवल इंटरफ़ेस का उपयोग करने की आवश्यकता है - आप इसे इस तरह से कर सकते हैं, हालांकि यह जटिलता और मामूली प्रदर्शन के मुद्दों को जोड़ता है:

public static IDisposable Subscribe<T>(Action<T> callback) where T : MyInterface 
{ 
    myActionList.Add(t => callback((T)t)); // this compiles and work 
    return null; 
} 
1

कक्षाएं और प्रतिनिधि थोड़ा अलग व्यवहार करते हैं। चलो एक सरल उदाहरण देखें:

public void SomeMethod<T>(T arg) where T : MyInterface 
{ 
    MyInterface e = arg; 
} 

इस विधि में आप मान सकते हैं कि कम से कम टी MyInterface होगा ताकि आप इस MyInterface e = arg; की तरह कुछ कर सकता है, क्योंकि आर्ग हमेशा MyInterface लिए डाली जा सकती है।

अब कैसे प्रतिनिधियों से व्यवहार देखते हैं:

public class BaseClass { }; 
public class DerivedClass : BaseClass { }; 
private readonly IList<Action<BaseClass >> myActionList = new List<Action<BaseClass>>(); 

public void Subscribe<T>(Action<T> callback) where T: BaseClass 
{ 
    myActionList.Add(callback); // so you could add more 'derived' callback here Action<DerivedClass> 
    return null; 
} 

myActionList को अब We'r जोड़ने DerivedClass कॉलबैक और फिर कहीं आप प्रतिनिधियों आह्वान:

foreach(var action in myActionList) { 
    action(new BaseClass); 
} 

लेकिन आप ऐसा नहीं कर सकते, क्योंकि यदि आपके पास DerivedClass कॉलबैक है तो आपको इसे DerivedClass पैरामीटर के रूप में पास करना होगा।

यह प्रश्न Covariance and contravariance को संदर्भित करता है। आप this आलेख से भिन्नता के बारे में पढ़ सकते हैं, एरिक लिपर्ट के पास भिन्नता के बारे में बहुत अंतरंग लेख हैं, this पहला लेख है, आप बाकी को अपने ब्लॉग में पा सकते हैं।

पीएस ली टिप्पणी के लिए संपादित accorind।

+0

कक्षाएं कॉन्वेंट नहीं हो सकती हैं - केवल प्रतिनिधियों और इंटरफेस में भिन्नताएं हो सकती हैं।यह प्रतिनिधियों के 'contravariance' कहने के लिए भी भ्रामक है - 'Func' प्रतिनिधि प्रकार उनके तर्क और covariant में उनके वापसी प्रकार में contravariant हैं। Contravariance प्रतिनिधि प्रकारों तक ही सीमित नहीं है, उदाहरण के लिए 'IObserver ' इंटरफ़ेस देखें, जो 'टी' में contravariant है। – Lee

+0

मैंने अपनी पोस्ट संपादित की है, धन्यवाद। – Andrew

2

मुझे इन स्थितियों में यह समझने में मदद मिलती है कि यदि आप व्यवहार की अनुमति देते हैं तो क्या गलत होता है। तो चलिए इसे मानते हैं।

interface IAnimal { void Eat(); } 
class Tiger : IAnimal 
{ 
    public void Eat() { ... } 
    public void Pounce() { ... } 
} 
class Giraffe : IAnimal 
... 
public void Subscribe<T>(Action<T> callback) where T: IAnimal 
{ 
    Action<IAnimal> myAction = callback; // doesn't compile but pretend it does. 
    myAction(new Giraffe()); // Obviously legal; Giraffe implements IAnimal 
} 
... 
Subscribe<Tiger>((Tiger t)=>{ t.Pounce(); }); 

तो क्या होता है? हम एक प्रतिनिधि बनाते हैं जो बाघ और पाउंस लेता है, Subscribe<Tiger> पर पास करें, इसे Action<IAnimal> में परिवर्तित करें, और फिर एक जिराफ पास करें, जो तब उछालता है।

स्पष्ट रूप से यह अवैध होना चाहिए। एकमात्र जगह जहां इसे अवैध बनाने के लिए समझदारी है Action<Tiger> से Action<IAnimal> में रूपांतरण है। तो यही वह जगह है जहां यह अवैध है।

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