2017-03-16 30 views
16

इस MCVE पर विचार करें:,प्रतिनिधि वापसी प्रकार लैम्ब्डा समारोह के साथ विभिन्न

using System; 

public interface IThing { } 

public class Foo : IThing 
{ 
    public static Foo Create() => new Foo(); 
} 

public class Bar : IThing 
{ 
    public static Bar Create() => new Bar(); 
} 

public delegate IThing ThingCreator(); 

class Program 
{ 
    static void Test(ThingCreator creator) 
    { 
     Console.WriteLine(creator.Method.ReturnType); 
    } 

    static void Main() 
    { 
     Test(Foo.Create);  // Prints: Foo 
     Test(Bar.Create);  // Prints: Bar 

     Test(() => new Foo()); // Prints: IThing 
     Test(() => new Bar()); // Prints: IThing 
    } 
} 

क्यों स्थिर कारखाने विधि के लिए वापसी प्रकार को दर्शाती है ठोस प्रकार दे, जबकि निर्माता इनलाइन बुला इंटरफ़ेस देता है? मैं उम्मीद करता हूं कि वे दोनों एक जैसा हों।

साथ ही, लैम्ब्डा संस्करण में निर्दिष्ट करने का कोई तरीका है कि मैं वापसी मूल्य को ठोस प्रकार होना चाहता हूं? या एक स्थैतिक विधि को ऐसा करने का एकमात्र तरीका बुला रहा है?

+0

एक __static__ विधि एकमात्र तरीका नहीं है। आप एक गैर स्थैतिक भी उपयोग कर सकते हैं। उदाहरण के लिए 'Func f =() => नया Foo(); टेस्ट (एफ। इन्वोक); लेकिन असली सवाल यह है कि, आप एक (संभावित रूप से मल्टी-कास्ट) प्रतिनिधि उदाहरण पर '.Method.ReturnType' क्यों जांचते हैं? इसके लिए क्या उपयोग है; तुम क्यो फिकर करते हो? –

+1

@ जेपे - एक तृतीय पक्ष लाइब्रेरी में एक सार्वजनिक एपीआई है जिसमें 'टेस्ट' जैसी विधि है। मुझे वास्तविक कार्य के संदर्भ के बजाय लैम्ब्डा पास करके साइड इफेक्ट की उम्मीद नहीं थी। यह पता लगाने के लिए खोदना था कि उन्होंने विधि के रिटर्न प्रकार की जांच की है। अगर यह मेरा अपना कोड था, तो मैं ऐसी चीज नहीं बनाऊंगा। –

उत्तर

12

लैम्ब्डा अभिव्यक्ति का रिटर्न प्रकार लैम्बडा को अनावश्यक रूप से लौटने से नहीं लिया जाता है, लेकिन इस प्रकार से यह आविष्कार करता है। यानी, आप (जब सामान्य प्रकार पैरामीटर involed कर रहे हैं को छोड़कर, एरिक Lippert की टिप्पणी देखें) इस तरह की एक लैम्ब्डा असाइन नहीं कर सकते:

// This generates the compiler error: 
// "Cannot assign lambda expression to an implicitly-typed variable". 
var lambda =() => new Foo(); 

तुम हमेशा कुछ इस तरह (lambdas हमेशा एक प्रतिनिधि प्रकार को सौंपा जाता है) करना चाहिए :

Func<MyType> lambda =() => new Foo(); 

इसलिए Test(() => new Foo()); में लैम्ब्डा की वापसी प्रकार पैरामीटर यह (IThing, ThingCreator की वापसी प्रकार) को सौंपा गया है के प्रकार से निर्धारित होता है।

Test(Foo.Create); में आपके पास लैम्ब्डा नहीं है, लेकिन public static Foo Create() ... के रूप में घोषित एक विधि है। यहां प्रकार स्पष्ट रूप से निर्दिष्ट किया गया है और Foo है (इससे कोई फर्क नहीं पड़ता कि यह एक स्थिर या उदाहरण विधि है)।

+5

मूल पोस्टर द्वारा दिए गए विशिष्ट प्रश्न को छोड़कर, मुझे लगता है कि आपका दावा है कि लैम्ब्डा अभिव्यक्ति का प्रकार * लैम्बडा रिटर्न के झूठ से कभी नहीं अनुमानित है। उदाहरण के लिए 'स्थिर शून्य एम (Func एफ)' 'एम (() => 1) के रूप में जाना जाता है।उस मामले में अनुमान के कारण औपचारिक पैरामीटर प्रकार - उनमें से सभी शून्य - ज्ञात हैं, और इसलिए टी को लैम्ब्डा के रिटर्न प्रकार से अनुमानित किया जा सकता है, जो int है। –

+0

@EricLippert: क्या यह तब होता है जब जेनेरिक प्रकार पैरामीटर शामिल होते हैं? –

+1

सही; मैंने जो मामला बताया है वह आईआईआरसी है, जो कि * लैम्ब्डा के रिटर्न प्रकार से * सी # इन्फर्स * है। अन्य सभी मामलों में रिटर्न प्रकार को प्रतिनिधि के रिटर्न प्रकार के साथ संगतता के लिए चेक किया जाता है, जिसे पहले से ही जाना जाना चाहिए। –

0

मेरा व्यक्तिगत अनुमान आमंत्रण स्थान है। जब आप () => new Foo() फ़ंक्शन में पास करते हैं तो यह इसे ThingCreator के रूप में पकड़ता है और इसे IThing प्राप्त करने के लिए आमंत्रित करता है। लेकिन, जब तुम Test विधि जब परीक्षा पद्धति यह आह्वान करने के लिए ठोस प्रकार के कारखाने विधि भेजने के लिए, यह करने के लिए चला जाता है ठोस प्रकार के Create() जो बदले में ठोस वस्तु जो पूरी तरह से स्वीकार्य है, क्योंकि यह भी IThing

मैं है रिटर्न अनुमान है कि आपको मेरे अनुमान से ज्यादा की आवश्यकता है। क्षमा करें अगर मैं गलत हूँ!

9

ओलिवियर का जवाब मूल रूप से सही है लेकिन यह कुछ अतिरिक्त स्पष्टीकरण का उपयोग कर सकता है।

स्थिर कारखाने विधि के लिए रिटर्न प्रकार को प्रतिबिंबित करने से कंक्रीट प्रकार क्यों मिलता है, जबकि कन्स्ट्रक्टर इनलाइन को इंटरफ़ेस देता है? मैं होना दोनों के लिए उन्हें उम्मीद होती है एक ही

आपका प्रोग्राम वर्ग निम्नलिखित वर्ग के बराबर है:

class Program 
{ 
    static void Test(ThingCreator creator) 
    { 
    Console.WriteLine(creator.Method.ReturnType); 
    } 
    static IThing Anon1() 
    { 
    return new Foo(); 
    } 
    static IThing Anon2() 
    { 
    return new Bar(); 
    } 
    static void Main() 
    { 
    Test(new ThingCreator(Foo.Create)); 
    Test(new ThingCreator(Bar.Create)); 
    Test(new ThingCreator(Program.Anon1)); 
    Test(new ThingCreator(Program.Anon2)); 
    } 
} 

और अब यह स्पष्ट किया जाना चाहिए क्यों कार्यक्रम प्रिंट यह क्या करता है।

कहानी यहाँ का नैतिक है कि जब हम lambdas के लिए छिपा विधि उत्पन्न करते हैं, उन छुपा तरीकों लौट जो कुछ प्रतिनिधि की आवश्यकता थी, और नहीं जो कुछ भी लैम्ब्डा रिटर्न है।

वह क्यों है?

एक और अधिक सार्थक उदाहरण है कि प्रदर्शित होगा:

static void Blah(Func<object> f) 
{ 
    Console.WriteLine(f().ToString()); 
} 
static void Main() 
{ 
    Blah(() => 123); 
} 

मुझे आशा है कि आप इस बात से सहमत है कि इस के रूप में

static object Anon() { return (object)123; } 

उत्पन्न किया जाना चाहिए और न

static int Anon() { return 123; } 

क्योंकि बाद नहीं किया जा सकता Func<object> में परिवर्तित! मुक्केबाजी निर्देश के लिए कहीं भी नहीं है, लेकिन ToString पर कॉल f() द्वारा संदर्भ प्रकार को वापस करने की अपेक्षा करता है।

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

साथ ही, लैम्ब्डा संस्करण में निर्दिष्ट करने का कोई तरीका है कि मैं वापसी मूल्य को ठोस प्रकार होना चाहता हूं? या एक स्थैतिक विधि को ऐसा करने का एकमात्र तरीका बुला रहा है?

निश्चित रूप से।

interface IThing {} 
class Foo : IThing {} 
delegate T ThingCreator<T>() where T : IThing; 
public class Program 
{ 
    static void Test<T>(ThingCreator<T> tc) where T : IThing 
    { 
    Console.WriteLine(tc.Method.ReturnType); 
    } 
    public static void Main() 
    { 
    Test(() => new Foo());  
    } 
} 

यह अलग क्यों है?

क्योंकि प्रकार निष्कर्ष deduces कि TFoo है और इसलिए हम ThingCreator<Foo> को लैम्ब्डा, लौट प्रकार Foo है जो परिवर्तित कर रहे हैं। इसलिए उत्पन्न विधि Foo लौटाती है, क्योंकि प्रतिनिधि प्रकार की उम्मीद है।

लेकिन रुकिए, आप कहते हैं ... मैं टेस्ट के हस्ताक्षर या ThingCreator

कोई चिंता नहीं की परिवर्तित नहीं कर सकता!

delegate T ThingCreator<T>() where T : IThing; 
delegate IThing ThingCreator();  
public class Program 
{ 
    static void Test(ThingCreator tc) 
    { 
     Console.WriteLine(tc.Method.ReturnType); 
    } 
    static ThingCreator DoIt<T>(ThingCreator<T> tc) where T : class, IThing 
    { 
     return tc.Invoke; 
    } 
    public static void Main() 
    { 
     Test(DoIt(() => new Foo())); 
    } 
} 

नीचे की ओर है कि अब अपने गैर सामान्य ThingCreator प्रतिनिधियों में से हर एक एक प्रतिनिधि जो एक ThingCreator<T> प्रतिनिधि, जो समय और स्मृति की बर्बादी का एक सा है कहता है है: आप अभी भी यह काम कर सकते हैं। लेकिन आपको वांछित वापसी प्रकार, और उस विधि के प्रतिनिधि को एक विधि बनाने के लिए टाइप अनुमान, विधि समूह रूपांतरण और लैम्ब्डा रूपांतरण मिलता है।

class बाधा नोट करें। क्या आप देखते हैं कि उस बाधा क्यों होनी चाहिए? यह पाठक के लिए एक अभ्यास के रूप में छोड़ दिया गया है।

+0

बहुत अच्छी व्याख्या। धन्यवाद! –

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