2014-09-17 8 views
48

पर विचार करें इस रिएक्टिव एक्सटेंशन झलकी (यह की व्यावहारिकता पर ध्यान न दें):थोड़ी देर लूप में ब्रेक जोड़ने से अधिभार अस्पष्टता कैसे हल हो जाती है?

return Observable.Create<string>(async observable => 
{ 
    while (true) 
    { 
    } 
}); 

यह (का उपयोग कर NuGet आरएक्स-मुख्य पैकेज) रिएक्टिव एक्सटेंशन 2.2.5 के साथ संकलन नहीं है। इसके साथ विफल रहता है:

Error 1 The call is ambiguous between the following methods or properties: 'System.Reactive.Linq.Observable.Create<string>(System.Func<System.IObserver<string>,System.Threading.Tasks.Task<System.Action>>)' and 'System.Reactive.Linq.Observable.Create<string>(System.Func<System.IObserver<string>,System.Threading.Tasks.Task>)'

हालांकि, जबकि पाश में कहीं भी एक break जोड़ने संकलन त्रुटि को ठीक:

return Observable.Create<string>(async observable => 
{ 
    while (true) 
    { 
     break; 
    } 
}); 

समस्या सभी (आसान पर रिएक्टिव एक्सटेंशन के बिना reproduced किया जा सकता है अगर आप कोशिश करना चाहते हैं यह) आरएक्स साथ नगण्य बिना:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Observable.Create<string>(async blah => 
     { 
      while (true) 
      { 
       Console.WriteLine("foo."); 
       break; //Remove this and the compiler will break 
      } 
     }); 
    } 
} 

public class Observable 
{ 
    public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, Task> subscribeAsync) 
    { 
     throw new Exception("Impl not important."); 
    } 

    public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, Task<Action>> subscribeAsync) 
    { 
     throw new Exception("Impl not important."); 
    } 
} 

public interface IObserver<T> 
{ 
} 

इसके बारे में रिएक्टिव एक्सटेंशन भाग की उपेक्षा कर, क्यों break सी # संकलक संकल्प जोड़ने में सहायता करता है अस्पष्टता? सी # विनिर्देश से ओवरलोड रिज़ॉल्यूशन के नियमों के साथ इसका वर्णन कैसे किया जा सकता है?

मैं दृश्य स्टूडियो 2013 अद्यतन 2 को निशाना बनाने 4.5.1 का उपयोग कर रहा हूँ।

+11

@ Mic1780: जब अनंत लूप का पता लगाने के दौरान संकलक विफल होते हैं? इससे पहले कभी नहीं देखा ... – knittl

+1

@ Mic1780 कोई विचार नहीं कि आप किस बारे में बात कर रहे हैं। सी # कंपाइलर (असल में, अधिकांश कंपाइलर्स) खुशी से एक अनंत लूप संकलित करेंगे, यहां तक ​​कि 'जबकि (सत्य) {} ' – tnw

उत्तर

59

यह सिर्फ यहाँ async के साथ-साथ lambdas बाहर निकलने के लिए, के रूप में यह जोर देती है क्या हो रहा है सबसे आसान है। इन तरीकों में से दोनों मान्य हैं और संकलन होगा:

public static void Foo() 
{ 
    while (true) { } 
} 
public static Action Foo() 
{ 
    while (true) { } 
} 

हालांकि, इन दोनों तरीकों के लिए:

public static void Foo() 
{ 
    while (true) { break; } 
} 
public static Action Foo() 
{ 
    while (true) { break; } 
} 

पहले compiles, और दूसरा नहीं है। इसमें एक कोड पथ है जो वैध मान वापस नहीं करता है।

वास्तव में, while(true){} (throw new Exception(); के साथ) यह एक दिलचस्प बयान है कि यह किसी भी प्रकार के रिटर्न प्रकार के साथ विधि का वैध निकाय है।

के बाद से अनंत लूप दोनों भार के लिए एक उपयुक्त उम्मीदवार है, और न तो अधिभार "बेहतर" है, यह एक अस्पष्टता त्रुटि का परिणाम है। गैर-अनंत लूप कार्यान्वयन में अधिभार संकल्प में केवल एक उपयुक्त उम्मीदवार है, इसलिए यह संकलित करता है।

बेशक

, async खेलने में वापस लाने के लिए, यह वास्तव में एक तरह से यहां के लिए प्रासंगिक है। async विधियों के लिए वे दोनों हमेशा कुछ लौटने के लिए, चाहे वह एक Task या एक Task<T> है। अधिभार समाधान के लिए "betterness" एल्गोरिदम, आपके मामले में प्रतिनिधियों कि void प्रतिनिधियों से अधिक कोई मान का चुनाव करेगा जब वहाँ एक लैम्ब्डा है कि या तो मेल खा सकते है, लेकिन दो अधिभार दोनों प्रतिनिधियों कि कोई मान है async तरीकों के लिए लौटने तथ्य Task<T> के बजाय Task एक वैल्यू वापस करने का वैचारिक समतुल्य उस betterness एल्गोरिदम में शामिल नहीं है। इस वजह से गैर-एसिंक समकक्ष के परिणामस्वरूप अस्पष्टता त्रुटि नहीं होगी, भले ही दोनों ओवरलोड लागू हों।

बेशक यह ध्यान देने योग्य है कि यह निर्धारित करने के लिए एक प्रोग्राम लिखना है कि कोड का मनमानी ब्लॉक कभी भी पूरा नहीं होगा, हालांकि संकलक सही ढंग से मूल्यांकन नहीं कर सकता है कि प्रत्येक स्निपेट पूरा हो जाएगा या नहीं, यह साबित हो सकता है इस तरह के कुछ साधारण मामले, कि कोड वास्तव में कभी पूरा नहीं होगा।इस वजह से कोड लिखने के तरीके हैं जो स्पष्ट रूप से (आपके और मेरे लिए) कभी पूरा नहीं होंगे, लेकिन संकलक संभवतः पूरा करने के साथ व्यवहार करेंगे।

+1

जितना सरल होगा, हालांकि' async' के लिए कुछ विशिष्ट है: दिया गया' शून्य फू (एक्शन ए); शून्य फू (Func f); ', 'Foo (() => {नया अपवाद फेंकें();}); } 'है * नहीं * संदिग्ध है और दूसरा अधिभार कॉल करेगा: भाषा का नमूना एक प्रतिनिधि के साथ रिटर्न प्रकार के पक्ष में है। लेकिन 'async' फ़ंक्शंस में वापसी का प्रकार हो सकता है भले ही वे वास्तव में ठोस मूल्य वापस नहीं लगते हैं: इसमें अभी भी' कार्य 'रिटर्न प्रकार हो सकता है। – hvd

+0

@ एचवीडी राइट, उस पर जाने के लिए एक पैराग्राफ में जोड़ा गया। – Servy

+1

दिलचस्प। तो 'जबकि (यह। प्रॉपर्टी Iswaysways) {} '(या' MethodReturnsTrue() ') एक निश्चित वापसी मूल्य के साथ एक विधि को फिर से हल करेगा (क्योंकि संकलक यह नहीं जान सकता कि संपत्ति क्या बदलेगी)। – knittl

24

async इस के साथ शुरू करने के लिए से बाहर ...

को तोड़ने के साथ छोड़कर, लैम्ब्डा अभिव्यक्ति के अंत पहुंचा जा सकता है, इसलिए लैम्ब्डा की वापसी प्रकार है void किया जाना है।

ब्रेक के बिना, लैम्ब्डा अभिव्यक्ति का अंत पहुंच योग्य नहीं है, इसलिए कोई वापसी प्रकार मान्य होगा। उदाहरण के लिए, इस ठीक है:

Func<string> foo =() => { while(true); }; 

इस जबकि नहीं है:

Func<string> foo =() => { while(true) { break; } }; 

तो break बिना, लैम्ब्डा अभिव्यक्ति एक एकल पैरामीटर के साथ किसी भी प्रतिनिधि प्रकार के लिए परिवर्तनीय होगा। break के साथ, लैम्ब्डा अभिव्यक्ति केवल एक पैरामीटर के साथ एक प्रतिनिधि प्रकार में परिवर्तनीय और void का रिटर्न प्रकार है।

async हिस्सा जोड़े और voidvoid या Task, बनाम void, Task या Task<T> किसी भी T जहां पहले आप किसी भी वापसी प्रकार हो सकता है के लिए हो जाता है। उदाहरण के लिए:

// Valid 
Func<Task<string>> foo = async() => { while(true); }; 
// Invalid (it doesn't actually return a string) 
Func<Task<string>> foo = async() => { while(true) { break; } }; 
// Valid 
Func<Task> foo = async() => { while(true) { break; } }; 
// Valid 
Action foo = async() => { while(true) { break; } }; 
संबंधित मुद्दे