2014-05-06 5 views
5

मुझे समझ नहीं आता क्यों सी # समाप्त होता है निम्नलिखित LINQPad कोड में एक गलत विस्तार विधि को क्रियान्वित करते हैं:"x => {throw ..} का Lambda ओवरलोडेड विधि में Func <T,Task> से मिलान करने के लिए अनुमानित है?

void Main() 
{ 
    // Actual: Sync Action 
    "Expected: Sync Action".Run(x => { x.Dump(); }); 

    // Actual: Async Task 
    "Expected: Async Task".Run(async x => { await System.Threading.Tasks.Task.Run(() => x.Dump()); }); 

    // Actual: Async Task!! 
    "Expected: Sync Action".Run(x => { throw new Exception("Meh"); }); 
} 

static class Extensions 
{ 
    public static void Run<T>(this T instance, Action<T> action) 
    { 
     "Actual: Sync Action".Dump(); 
     action(instance); 
    } 

    public static void Run<T>(this T instance, Func<T, System.Threading.Tasks.Task> func) 
    { 
     "Actual: Async Task".Dump(); 
     func(instance).Wait(); 
    } 
} 

संकलक क्यों लगता है कि लैम्ब्डा यहाँ एक टास्क देता है?

मुझे तीसरे कॉल में रन() को "वास्तविक: सिंक एक्शन" देखने की उम्मीद है क्योंकि लैम्ब्डा में कुछ भी इंगित करता है कि यह एक फंक रिटर्निंग टास्क है।

+0

यह लौटने शून्य है LINQPad कोड .. डंप() विस्तार विधि के अलावा विशेष कुछ नहीं है। – ichen

+1

आप सही हैं, यह वास्तव में एक्सटेंशन विधि से असंबंधित है क्योंकि मैं सामान्य अधिभारित विधियों के साथ पुन: पेश करने में सक्षम था। – ichen

उत्तर

2

यह केवल एक अधिभार समाधान समस्या है। जाहिर है, लैम्ब्डा x => { throw new Exception("Meh"); } या तो Action<T> या Func<T, SomeNonVoidType> (साथ ही साथ कई अन्य प्रतिनिधि प्रकारों को इस प्रश्न के लिए अप्रासंगिक) में परिवर्तित किया जा सकता है। यह केवल सी # के अधिभार संकल्प नियम है जो इस मामले में बाद वाले को पसंद करते हैं।

void Main() 
{ 
    // Output: Func<T, int> 
    "Test".WhatsThis(x => { throw new Exception("Meh"); }); 
} 

static class Extensions 
{ 
    public static void WhatsThis<T>(this T dummy, Action<T> action) 
    { 
     "Action<T>".Dump(); 
    } 
    public static void WhatsThis<T>(this T dummy, Func<T, int> func) 
    { 
     "Func<T, int>".Dump(); 
    } 
} 

के लिए के रूप में क्यों यह है मामला है, मैं 100% यकीन नहीं है, लेकिन spec पर एक आकस्मिक देखो मुझे नीचे संभावित विश्लेषण से पता चलता (जोर:

यहाँ एक अधिक प्रतिनिधि उदाहरण है मेरा):

7.5.3 अधिभार संकल्प

[...]

7.5.3.3 अभिव्यक्ति से बेहतर रूपांतरण

एक अंतर्निहित रूपांतरण सी 1 को देखते हुए जो अभिव्यक्ति ई से एक प्रकार टी 1 में परिवर्तित होता है, और एक अंतर्निहित रूपांतरण सी 2 जो अभिव्यक्ति ई से एक प्रकार टी 2, सी 1 में परिवर्तित होता है, एक बेहतर रूपांतरण है सी 2 से यदि निम्न में से कम से कम एक रखती है:

[...]

• ई एक गुमनाम समारोह है, टी 1 या तो एक प्रतिनिधि प्रकार डी 1 या एक अभिव्यक्ति पेड़ प्रकार Expression<D1>, टी 2 या तो एक है प्रतिनिधि प्रकार डी 2 या एक अभिव्यक्ति वृक्ष टाइप Expression<D2> और निम्न में से एक है:

[...]

• डी 1 एक वापसी प्रकार Y है, और डी 2

+0

यह बताने के लिए धन्यवाद कि क्या हो रहा है। ऊपर दिए गए दूसरे आमंत्रण में मैं स्पष्ट रूप से "async x =>" निर्दिष्ट करता हूं जो संकलक को संकेत दे सकता है कि हम यहां एक लैम्ब्डा लौटने वाले कार्य से निपट रहे हैं। एसिंक कीवर्ड की अनुपस्थिति में, क्या यह कार्रवाई के बजाए func को "डिफ़ॉल्ट" समझ में आता है या क्या यह सिर्फ मनमाना विकल्प है? – ichen

+0

स्पष्ट रूप से, इस संदर्भ में एक शून्य के मुकाबले एक गैर-शून्य रिटर्निंग प्रतिनिधि को "बेहतर"/"अधिक विशिष्ट" के रूप में लगता है। यह किसी भी संकेत प्रदान नहीं करता है कि क्यों, हालांकि। मुझे संदेह है कि एनोटेटेड विनिर्देश अधिक प्रकट कर सकता है (मेरे पास एक प्रतिलिपि नहीं है, दुख की बात है)। अगर एरिक लिपर्ट जैसे कोई व्यक्ति इस सवाल को देखता है, तो बाधाएं अच्छी होती हैं कि हमें एक और निश्चित स्पष्टीकरण मिल सकता है। – Ani

+0

@ichen: 'async' कीवर्ड लैम्ब्डा के प्रकार को प्रभावित नहीं करता है।मेरा मानना ​​है कि 'से बचने के लिए अधिभार संकल्प के इरादे नियम हैं async void' lambdas, [जो विशेष रूप से मुश्किल कर रहे हैं] (http://blogs.msdn.com/b/pfxteam/archive/2012/02/08/10265476.aspx)। –

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