2009-07-13 22 views
25

में ईवेंट और प्रतिनिधि contravariance this question की जांच करते समय मुझे उत्सुकता है कि सी # 4.0 में नई कॉन्वर्सिस/contravariance सुविधाओं को कैसे प्रभावित करेगा।.NET 4.0 और C# 4.0

बीटा 1 में, सी # सीएलआर से असहमत प्रतीत होता है। में वापस सी # 3.0, यदि आप था:

public event EventHandler<ClickEventArgs> Click; 

... और फिर कहीं और आप था:

button.Click += new EventHandler<EventArgs>(button_Click); 

... संकलक barf क्योंकि वे असंगत प्रतिनिधि प्रकार रहे हैं। लेकिन सी # 4.0 में, यह ठीक से संकलित करता है, क्योंकि सीएलआर 4.0 में टाइप पैरामीटर अब in के रूप में चिह्नित किया गया है, इसलिए यह contravariant है, और इसलिए संकलक मल्टीकास्ट प्रतिनिधि += काम करेगा।

यहाँ मेरी परीक्षा है:

public class ClickEventArgs : EventArgs { } 

public class Button 
{ 
    public event EventHandler<ClickEventArgs> Click; 

    public void MouseDown() 
    { 
     Click(this, new ClickEventArgs()); 
    } 
} 

class Program 
{  
    static void Main(string[] args) 
    { 
     Button button = new Button(); 

     button.Click += new EventHandler<ClickEventArgs>(button_Click); 
     button.Click += new EventHandler<EventArgs>(button_Click); 

     button.MouseDown(); 
    } 

    static void button_Click(object s, EventArgs e) 
    { 
     Console.WriteLine("Button was clicked"); 
    } 
} 

लेकिन हालांकि यह संकलित है, यह क्रम में काम नहीं करता है (ArgumentException: प्रतिनिधि एक ही प्रकार के होना चाहिए)।

यह ठीक है अगर आप केवल दो प्रतिनिधि प्रकारों में से एक जोड़ते हैं। लेकिन मल्टीकास्ट में दो अलग-अलग प्रकार के संयोजन अपवाद का कारण बनता है जब दूसरा जोड़ा जाता है।

मुझे लगता है कि यह बीटा 1 में सीएलआर में एक बग है (संकलक का व्यवहार उम्मीदपूर्वक सही दिखता है)। रिलीज़ उम्मीदवार के लिए

अद्यतन:

ऊपर कोड नहीं रह गया है संकलित करता है। यह होना चाहिए कि प्रतिनिधि प्रकार में TEventArgs का contravariance वापस ले लिया गया है, इसलिए अब प्रतिनिधि के पास .NET 3.5 की समान परिभाषा है।

है, बीटा मैं कम से रहा होगा देखा:

public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e); 

अब यह करने के लिए वापस आ गया है:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); 

लेकिन Action<T> प्रतिनिधि पैरामीटर T अभी भी contravariant है:

public delegate void Action<in T>(T obj); 

वही Func<T> केके लिए जाता हैकॉन्वर्सेंट होने के नाते।

यह समझौता बहुत समझ में आता है, जब तक हम मानते हैं कि मल्टीकास्ट प्रतिनिधियों का प्राथमिक उपयोग घटनाओं के संदर्भ में है। मैंने व्यक्तिगत रूप से पाया है कि मैं कभी भी घटनाओं को छोड़कर मल्टीकास्ट प्रतिनिधियों का उपयोग नहीं करता हूं।

तो मुझे लगता है कि सी # कोडिंग मानकों को अब एक नया नियम अपनाया जा सकता है: बहुविकल्पीय प्रतिनिधियों को कॉन्वर्सिस/contravariance से संबंधित कई प्रतिनिधि प्रकारों से नहीं बनाते हैं। और यदि आपको नहीं पता कि इसका क्या अर्थ है, तो सुरक्षित पक्ष पर होने वाली घटनाओं के लिए Action का उपयोग करने से बचें।

बेशक, यह निष्कर्ष the original question that this one grew from के लिए निहितार्थ हैं ...

+1

Thoiugh दिलचस्प सवाल है? –

+0

मुझे आश्चर्य है कि इस छेद ने सी # टीम के नोटिस से बच निकला, जेनेरिक प्रतिनिधियों के लिए भिन्नता शुरू करने के बाद पहली चीजों में से एक होना चाहिए, है ना? सी # 5 इसे भी प्रदर्शित करता है (क्लियर संस्करण वही है)। – nawfal

उत्तर

0

तुम दोनों से ArgumentException हो रही है?अगर अपवाद को केवल नए हैंडलर द्वारा फेंक दिया जा रहा है, तो मुझे लगता है कि यह पिछड़ा-संगत है।

बीटीडब्ल्यू, मुझे लगता है कि आपकी टिप्पणियां मिश्रित हैं। सी # 3.0 इस में:

button.Click += new EventHandler<EventArgs>(button_Click); // old

रन नहीं होती। यह सी # 4.0

+0

मैंने प्रश्न में वर्जनिंग के सभी संदर्भ हटा दिए हैं, ऐसा लगता है कि ऐसा लगता है कि आपने भ्रम पैदा किया है। इस मुद्दे से संबंधित नए और पुराने के बारे में टिप्पणियां मैं मूल रूप से सोच रहा था, जिसे मैंने इस प्रश्न से अलग किया था। इस प्रश्न के पास प्रति पिछड़ा संगतता के साथ कुछ लेना देना नहीं है।यह संकलक के बारे में है कि एक मल्टीकास्ट प्रतिनिधि contravariant प्रकारों के तरीकों से बांध सकता है, जो तब रनटाइम पर सच नहीं है। –

9

बहुत दिलचस्प है। ऐसा होने के लिए आपको घटनाओं का उपयोग करने की आवश्यकता नहीं है, और वास्तव में मुझे सरल प्रतिनिधियों का उपयोग करना आसान लगता है।

Func<string> और Func<object> पर विचार करें। सी # 4.0 में आप Func<string> को Func<object> में निहित रूप से रूपांतरित कर सकते हैं क्योंकि आप ऑब्जेक्ट संदर्भ के रूप में हमेशा एक स्ट्रिंग संदर्भ का उपयोग कर सकते हैं। हालांकि, जब आप उन्हें गठबंधन करने का प्रयास करते हैं तो चीजें गलत होती हैं।

using System; 

class Program 
{  
    static void Main(string[] args) 
    { 
     Func<string> stringFactory =() => "hello"; 
     Func<object> objectFactory =() => new object(); 

     Func<object> multi1 = stringFactory; 
     multi1 += objectFactory; 

     Func<object> multi2 = objectFactory; 
     multi2 += stringFactory; 
    }  
} 

यह ठीक संकलित, लेकिन Combine कॉल के दोनों (+ = वाक्यात्मक चीनी से छिपा हुआ) अपवाद फेंक: यहाँ एक छोटा लेकिन पूरा दो अलग अलग तरीकों समस्या का प्रदर्शन कार्यक्रम है। (दूसरे को देखने के लिए पहले व्यक्ति को टिप्पणी करें।)

यह निश्चित रूप से एक समस्या है, हालांकि मुझे बिल्कुल यकीन नहीं है कि समाधान क्या होना चाहिए। यह संभव है कि निष्पादन समय पर प्रतिनिधि कोड को शामिल प्रतिनिधि प्रकारों के आधार पर उपयोग करने के लिए सबसे उपयुक्त प्रकार का उपयोग करने की आवश्यकता होगी। यह थोड़ा बुरा है। सामान्य Delegate.Combine कॉल होना बहुत अच्छा होगा, लेकिन आप वास्तव में प्रासंगिक प्रकारों को सार्थक तरीके से व्यक्त नहीं कर सके।

एक बात है कि ध्यान देने योग्य है कि covariant रूपांतरण एक संदर्भ रूपांतरण है - ऊपर, में multi1 और stringFactory एक ही वस्तु का संदर्भ लें: यह (

Func<object> multi1 = new Func<object>(stringFactory); 

लेखन के रूप में नहीं ही है कि पर बिंदु, निम्न पंक्ति कोई अपवाद के साथ निष्पादित नहीं होगी।) निष्पादन समय पर, बीसीएल को वास्तव में Func<string> और Func<object> को संयुक्त किया जाना चाहिए; इसके पास जाने के लिए कोई अन्य जानकारी नहीं है।

यह बुरा है, और मुझे गंभीरता से उम्मीद है कि यह किसी भी तरह से तय हो जाएगा। मैं इस प्रश्न के लिए मैड्स और एरिक को सतर्क करूंगा ताकि हम कुछ और सूचित टिप्पणी प्राप्त कर सकें।

+0

कूल, मैं ट्रेन के घर पर इसके साथ टंकण करते समय व्यावहारिक रूप से एक ही उदाहरण कोड पहुंचा। Gnarly विवरण सुनने के लिए बहुत दिलचस्प होगा। –

1

मुझे बस इसे अपने आवेदन में ठीक करना पड़ा। मैंने निम्नलिखित किया:

// variant delegate with variant event args 
MyEventHandler<<in T>(object sender, IMyEventArgs<T> a) 

// class implementing variant interface 
class FiresEvents<T> : IFiresEvents<T> 
{ 
    // list instead of event 
    private readonly List<MyEventHandler<T>> happened = new List<MyEventHandler<T>>(); 

    // custom event implementation 
    public event MyEventHandler<T> Happened 
    { 
     add 
     { 
      happened.Add(value); 
     } 
     remove 
     { 
      happened.Remove(value); 
     } 
    } 

    public void Foo() 
    { 
     happened.ForEach(x => x.Invoke(this, new MyEventArgs<T>(t)); 
    } 
} 

मुझे नहीं पता कि नियमित बहु-कलाकारों के कार्यक्रमों में प्रासंगिक मतभेद हैं या नहीं। जहां तक ​​मैंने इसका इस्तेमाल किया, यह काम करता है ...

वैसे: I never liked the events in C#। मुझे समझ में नहीं आता कि भाषा की सुविधा क्यों है, जब यह कोई लाभ नहीं प्रदान करता है।

+1

प्राथमिक अंतर यह है कि मल्टीकास्ट प्रतिनिधि अपरिवर्तनीय हैं, इसलिए यह आमंत्रण के दौरान हैंडलर जोड़ने/निकालने के लिए थ्रेड-सुरक्षित है। आपके कार्यान्वयन में, सूची अप्रत्याशित परिणामों के साथ ईवेंट आमंत्रण के दौरान उत्परिवर्तित हो सकती है। –

+0

@ सोरेन बोइसेन: बहुत अच्छा बिंदु। इस के लिए धन्यवाद। मैं सूची के बजाय 'ConcurrentBag ' का उपयोग कर सकता था। –

+1

एक अन्य विकल्प http://blogs.msdn.com/b/dotnet/archive/2013/09/25/immutable-collections-ready-for-prime-time.aspx से ImmutableArray या ImmutableList का उपयोग कर रहा है। यह प्रेषण बनाम जोड़ने/निकालने के लिए अनुकूल होगा, लेकिन अधिक महत्वपूर्ण बात यह है कि वे पीसीएल परियोजनाओं में उपलब्ध हैं :-) –