अनाम विधि को var में क्यों नहीं सौंपा जा सकता है?


139

मेरे पास निम्नलिखित कोड हैं:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

हालाँकि, निम्नलिखित संकलन नहीं करता है:

var comparer = delegate(string value) {
    return value != "0";
};

संकलक यह पता नहीं लगा सकता कि यह क्यों है Func<string, bool>? यह एक स्ट्रिंग पैरामीटर लेता है, और एक बूलियन देता है। इसके बजाय, यह मुझे त्रुटि देता है:

अनाम रूप से टाइप किए गए स्थानीय चर में अनाम विधि असाइन नहीं कर सकता।

मेरे पास एक अनुमान है और वह यह है कि अगर var संस्करण संकलित किया गया है , तो यदि मेरे पास यह हो तो इसमें स्थिरता की कमी होगी:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

ऊपर से कोई मतलब नहीं होगा क्योंकि फंक <> केवल 4 तर्क (.NET 3.5 में, जो मैं उपयोग कर रहा हूं) की अनुमति देता है। शायद कोई इस समस्या को स्पष्ट कर सकता है। धन्यवाद।


3
अपने 4 तर्क तर्क के बारे में ध्यान दें। .NET 4 में, Func<>16 तर्कों को स्वीकार करता है।
एंथनी पेग्राम

स्पष्टीकरण के लिए धन्यवाद। मैं .NET 3.5 का उपयोग कर रहा हूं।
मार्लोन

9
कंपाइलर यह क्यों सोचते हैं कि ए Func<string, bool>? यह Converter<string, bool>मेरे लिए एक लग रहा है!
बेन वोइगट


3
कभी-कभी मुझे VB की याद आती है ..Dim comparer = Function(value$) value <> "0"
Slai

जवाबों:


155

दूसरों ने पहले से ही बताया है कि असीम रूप से कई संभावित प्रतिनिधि प्रकार हैं जो आपके पास हो सकते हैं; ऐसी क्या खास बात के बारे में क्या है Funcकि यह बजाय डिफ़ॉल्ट होने के लिए योग्य है Predicateया Actionया किसी अन्य संभावना? और, लंबोदर के लिए, यह क्यों स्पष्ट है कि अभिव्यक्ति के पेड़ के रूप के बजाय प्रतिनिधि प्रतिनिधि चुनने का इरादा है?

लेकिन हम कह सकते हैं कि Funcयह विशेष है, और यह है कि लैंबडा या अनाम विधि का अनुमानित प्रकार किसी चीज का फन है। हमें अभी भी सभी प्रकार की समस्याएं हैं। निम्नलिखित मामलों के लिए आप किस प्रकार का अनुमान लगाना चाहेंगे?

var x1 = (ref int y)=>123;

ऐसा कोई Func<T>प्रकार नहीं है जो किसी भी चीज़ को लेता है।

var x2 = y=>123;

हम औपचारिक पैरामीटर के प्रकार को नहीं जानते हैं, हालांकि हम रिटर्न को जानते हैं। (या हम करते हैं? वापसी int है? लंबी? छोटी बाइट?)

var x3 = (int y)=>null;

हम वापसी प्रकार नहीं जानते, लेकिन यह शून्य नहीं हो सकता। वापसी प्रकार कोई भी संदर्भ प्रकार या कोई अशक्त मान प्रकार हो सकता है।

var x4 = (int y)=>{ throw new Exception(); }

फिर, हम रिटर्न प्रकार नहीं जानते हैं, और इस बार यह शून्य हो सकता है।

var x5 = (int y)=> q += y;

क्या यह एक शून्य-रिटर्न स्टेटमेंट लंबोदा या कुछ और है जो कि क्यू को सौंपा गया मान लौटाता है? दोनों कानूनी हैं; हमें कौन सा चुनना चाहिए?

अब, आप कह सकते हैं, ठीक है, बस उन सुविधाओं में से किसी का समर्थन नहीं करते। बस "सामान्य" मामलों का समर्थन करें जहां प्रकारों पर काम किया जा सकता है। यह मदद नहीं करता है। यह कैसे मेरे जीवन को आसान बनाता है? यदि सुविधा कभी-कभी काम करती है और कभी-कभी विफल हो जाती है, तो मुझे अभी भी उन सभी असफल स्थितियों का पता लगाने के लिए कोड लिखना होगा और प्रत्येक के लिए एक सार्थक त्रुटि संदेश देना होगा । हमें अभी भी उस सभी व्यवहार को निर्दिष्ट करना है, दस्तावेज करना है, इसके लिए परीक्षण लिखना है, और इसी तरह। यह एक बहुत महंगी सुविधा है जो उपयोगकर्ता को शायद आधा दर्जन कीस्ट्रोक्स बचाता है। हमारे पास भाषा को बेहतर तरीके से जोड़ने का बेहतर तरीका है कि एक फीचर के लिए परीक्षण के मामलों को लिखने में बहुत समय खर्च किया जाए जो आधे समय तक काम नहीं करता है और उन मामलों में शायद ही कोई लाभ प्रदान करता है जहां यह काम करता है।

स्थिति जहां यह वास्तव में उपयोगी है:

var xAnon = (int y)=>new { Y = y };

क्योंकि उस चीज़ के लिए कोई "बोलने योग्य" प्रकार नहीं है। लेकिन हमारे पास हर समय यह समस्या होती है, और हम प्रकार घटाने के लिए केवल विधि प्रकार का उपयोग करते हैं:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

और अब विधि प्रकार का निष्कर्ष यह बताता है कि फंक प्रकार क्या है।


43
जब आप अपने एसओ के उत्तर एक पुस्तक में संकलित करने जा रहे हैं? मैं इसे खरीदूंगा :)
मैट ग्रीर

13
मैं SO के उत्तरों की एरिक लिपर्ट पुस्तक के लिए दूसरा प्रस्ताव रखता हूं। सुझाया गया शीर्षक: "स्टैक से प्रतिबिंब"
एडम रैकिस

24
@ ईरिक: अच्छा जवाब है, लेकिन इसे थोड़ा भ्रामक समझना है क्योंकि यह संभव नहीं है, क्योंकि यह वास्तव में डी में पूरी तरह से ठीक काम करता है। यह सिर्फ यह है कि आप लोग प्रतिनिधियों को अपने स्वयं के प्रकार देने के लिए नहीं चुनते हैं, और इसके बजाय उन्हें निर्भर करते हैं। उनके संदर्भों पर ... तो IMHO का जवाब "होना चाहिए क्योंकि यह है कि हमने इसे कैसे बनाया" कुछ और की तुलना में अधिक। :)
user541686

5
@abbriddissonance I ध्यान दें कि संकलक खुला स्रोत है। यदि आप इस सुविधा की परवाह करते हैं तो आप इसे करने के लिए आवश्यक समय और प्रयास दान कर सकते हैं। मैं आपको एक पुल अनुरोध प्रस्तुत करने के लिए प्रोत्साहित करता हूं।
एरिक लिपर्ट

7
@ एब्रेटडिसनेंस: हमने सीमित संसाधनों के संदर्भ में लागत को मापा: डेवलपर्स और समय। यह जिम्मेदारी भगवान ने नहीं दी थी; यह डेवलपर डिवीजन के उपाध्यक्ष द्वारा लगाया गया था। धारणा है कि किसी तरह सी # टीम एक बजट प्रक्रिया को अनदेखा कर सकती है एक अजीब है। मैं आपको विश्वास दिलाता हूं, ट्रेडऑफ़ थे और अभी भी विशेषज्ञों के सावधानीपूर्वक, विचारशील विचार से बने हैं, जिनके पास सी # समुदाय ने इच्छाएं व्यक्त की थीं, माइक्रोसॉफ्ट के लिए रणनीतिक मिशन और डिजाइन में उनके स्वयं के उत्कृष्ट स्वाद को ध्यान में रखते हुए।
एरिक लिपर्ट

29

केवल एरिक लिपर्ट निश्चित रूप से जानता है, लेकिन मुझे लगता है कि यह इसलिए है क्योंकि प्रतिनिधि प्रकार के हस्ताक्षर विशिष्ट रूप से प्रकार निर्धारित नहीं करते हैं।

अपने उदाहरण पर विचार करें:

var comparer = delegate(string value) { return value != "0"; };

यहाँ क्या varहोना चाहिए के लिए दो संभव निष्कर्ष हैं:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

कंपाइलर को किसका अनुमान लगाना चाहिए? एक या दूसरे को चुनने का कोई अच्छा कारण नहीं है। और यद्यपि एक Predicate<T>कार्यात्मक रूप से एक के बराबर है Func<T, bool>, वे अभी भी .NET प्रकार के सिस्टम के स्तर पर भिन्न प्रकार के हैं। इसलिए कंपाइलर डेलिगेट प्रकार को हल नहीं कर सकता है, और टाइप इंफ़ेक्शन को विफल करना चाहिए।


1
मुझे यकीन है कि Microsoft के कुछ अन्य लोग भी निश्चित रूप से जानते हैं। ;) लेकिन हाँ, आप एक प्रमुख कारण से सहमत हैं, संकलन समय प्रकार निर्धारित नहीं किया जा सकता है क्योंकि कोई भी नहीं है। भाषा विनिर्देश की धारा 8.5.1 विशेष रूप से गुमनाम कार्यों को अव्यवस्थित रूप से टाइप किए गए चर घोषणाओं में उपयोग किए जाने से रोकने के लिए इस कारण को उजागर करती है।
एंथनी पेग्राम

3
हां। और इससे भी बदतर, लंबोदर के लिए हम यह भी नहीं जानते कि क्या यह एक प्रतिनिधि प्रकार में जा रहा है; यह एक अभिव्यक्ति वृक्ष हो सकता है।
एरिक लिपर्ट

किसी को भी रुचि के लिए, मैं इस बारे में और अधिक थोड़ा लिखा था कि कैसे सी # और एफ # पर विपरीत दृष्टिकोण mindscapehq.com/blog/index.php/2011/02/23/...
itowlson

कंपाइलर सिर्फ एक नया अनोखा प्रकार क्यों नहीं बना सकता है जैसे C ++ अपने लंबो फंक्शन के लिए करता है
वेपेंग एल

वे ".NET प्रकार सिस्टम के स्तर पर" कैसे भिन्न होते हैं?
arao6

6

एरिक लिपर्ट के पास इसके बारे में एक पुरानी पोस्ट है जहां वह कहते हैं

और वास्तव में सी # 2.0 विनिर्देश इसे बाहर कहते हैं। विधि समूह अभिव्यक्तियाँ और अनाम विधि अभिव्यक्तियाँ C # 2.0 में टाइपलेस एक्सप्रेशन हैं, और लैम्ब्डा अभिव्यक्तियाँ उन्हें C # 3.0 में जोड़ती हैं। इसलिए उनके लिए एक निहित घोषणा के दाहिने हाथ की ओर "नग्न" दिखाई देना अवैध है।


और यह भाषा विनिर्देश के खंड 8.5.1 द्वारा रेखांकित किया गया है। "प्रारंभिक अभिव्यक्ति में एक संकलित समय प्रकार होना चाहिए" ताकि एक अंतर्निहित टाइप किए गए स्थानीय चर का उपयोग किया जा सके।
एंथनी पेग्राम

5

विभिन्न प्रतिनिधियों को अलग-अलग प्रकार माना जाता है। उदाहरण के लिए, Actionकी तुलना में अलग है MethodInvoker, और एक उदाहरण Actionप्रकार के एक चर को नहीं सौंपा जा सकता है MethodInvoker

तो, जैसे एक अनाम प्रतिनिधि (या लैम्ब्डा) दिया जाता है () => {}, क्या यह एक Actionहै MethodInvoker? संकलक नहीं बता सकता।

इसी तरह, यदि मैं एक प्रतिनिधि प्रकार को एक stringतर्क देने और एक boolको वापस लेने की घोषणा करता हूं, तो संकलक को कैसे पता चलेगा कि आप वास्तव Func<string, bool>में मेरे प्रतिनिधि प्रकार के बजाय चाहते थे ? यह प्रतिनिधि प्रकार का अनुमान नहीं लगा सकता है।


2

निम्नलिखित बिंदु MSDN से स्थानीय रूप से टाइप किए गए स्थानीय चर के बारे में हैं:

  1. var का उपयोग केवल तब किया जा सकता है जब एक स्थानीय चर घोषित किया जाता है और एक ही कथन में आरंभ किया जाता है; चर को शून्य, या विधि समूह या अनाम फ़ंक्शन के लिए प्रारंभ नहीं किया जा सकता है।
  2. वैर कीवर्ड कंपाइलर को निर्देश देता है कि वह इनिशियलाइज़ेशन स्टेटमेंट के दाईं ओर एक्सप्रेशन से वेरिएबल के प्रकार का अनुमान लगाए।
  3. यह समझना महत्वपूर्ण है कि var कीवर्ड का अर्थ "variant" नहीं है और यह इंगित नहीं करता है कि वेरिएबल शिथिल टाइप है, या लेट-बाउंड है। इसका सिर्फ यह मतलब है कि संकलक सबसे उपयुक्त प्रकार निर्धारित करता है और असाइन करता है।

MSDN संदर्भ: स्थानीय रूप से टाइप किए गए स्थानीय चर

अनाम तरीकों के संबंध में निम्नलिखित पर विचार:

  1. अनाम विधियाँ आपको पैरामीटर सूची से बाहर निकलने में सक्षम बनाती हैं।

MSDN संदर्भ: बेनामी तरीके

मुझे संदेह होगा कि चूंकि अनाम विधि में वास्तव में अलग-अलग हस्ताक्षर हो सकते हैं, इसलिए संकलक ठीक से अनुमान लगाने में असमर्थ है कि असाइन करने के लिए सबसे उपयुक्त प्रकार क्या होगा।


1

मेरी पोस्ट वास्तविक प्रश्न का उत्तर नहीं देती है, लेकिन यह अंतर्निहित प्रश्न का उत्तर देती है:

"मैं कैसे कुछ भद्दे टाइप टाइप करने से बचता हूँ Func<string, string, int, CustomInputType, bool, ReturnType>?"[1]

आलसी / हैकी प्रोग्रामर होने के नाते जो मैं हूं, मैंने प्रयोग के साथ प्रयोग किया Func<dynamic, object> - जो एक एकल इनपुट पैरामीटर लेता है और एक वस्तु देता है।

कई तर्कों के लिए, आप इसका उपयोग इस तरह कर सकते हैं:

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

युक्ति: आप उपयोग कर सकते हैं Action<dynamic> यदि आप किसी ऑब्जेक्ट को वापस करने की आवश्यकता नहीं है, तो आप ।

हाँ, मुझे पता है कि यह संभवतः आपके प्रोग्रामिंग सिद्धांतों के खिलाफ है, लेकिन इससे मुझे और शायद कुछ पायथन कोडर्स को समझ में आता है।

मैं प्रतिनिधियों में बहुत नौसिखिया हूँ ... बस जो मैंने सीखा था उसे साझा करना चाहता था।


[१] यह मानता है कि आप एक ऐसी विधि नहीं कह रहे हैं जिसके लिए Funcएक पैरामीटर के रूप में पूर्वनिर्धारित की आवश्यकता होती है , इस स्थिति में, आपको उस स्ट्रिंग को टाइप करना होगा: /


0

वह कैसे है?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.