2017-01-13 6 views
5

के साथ काम नहीं करता है क्या यह एक कंपाइलर बग है या क्या कोई विशिष्ट कारण है कि शून्य-सशर्त ऑपरेटर सामान्य तरीकों के अंदर Func के साथ काम नहीं करता है?शून्य-सशर्त ऑपरेटर फनक <T> के साथ एक सामान्य विधि

एक उदाहरण निम्नलिखित संकलन नहीं है देने के लिए

public static T Test<T>(Func<T> func) 
{ 
    return func?.Invoke() ?? default(T); 
} 

त्रुटि संकलक का उत्पादन CS0023 Operator '?' cannot be applied to operand of type 'T'

है मुझे पता है कि आप इस हालांकि कर एक ही प्राप्त कर सकते हैं कर रहा हूँ:

public static T Test<T>(Func<T> func) 
{ 
    return func != null ? func() : default(T); 
} 

तो ऐसा क्यों है कि इसकी अनुमति नहीं है?

आगे Action<T> विस्तृत करने के लिए अपेक्षा के अनुसार काम करता है।

public static void Test<T>(Action<T> action, T arg) 
{ 
    action?.Invoke(arg); 
} 

अद्यतन (2017/01/17):

कुछ और अनुसंधान के बाद, यह भी कम समझ में आता है, यहां तक ​​कि निम्नलिखित के साथ:

मान लीजिए कि हम एक वर्ग करते हैं (संदर्भ प्रकार)

public class Foo 
{ 
    public int Bar { get; set; } 
} 

और मान लीजिए कि हम एक Func<int>

012 डालते हैं
Func<int> fun =() => 10; 

निम्नलिखित काम करता है:

// This work 
var nullableBar = foo?.Bar; // type of nullableBar is int? 
var bar = nullableBar ?? default(int); // type of bar is int 

// And this work 
nullableBar = fun?.Invoke(); // ditto 
bar = nullableBar ?? default(int); // ditto 

कौन सा तर्क के अनुसार अर्थ है वहाँ लागू किया गया फिर एक मूल्य के प्रकार null-conditional और null-coalescing ऑपरेटर्स का उपयोग कर काम करना चाहिए की एक Func<T>

हालांकि जैसे ही बाएं हाथ null-conditional के जेनेरिक प्रकार बगैर किसी अड़चन के साथ सामान्य है तो यह एक ही तर्क है कि यह यह दोनों मूल्य-प्रकार के लिए एक ही तर्क लागू कर सकते हैं पर विचार करने के लिए सक्षम होना चाहिए लागू नहीं कर सकते और संदर्भ-प्रकार जब प्रकार स्पष्ट रूप से लागू होते हैं।

मुझे कंपेलरों की बाधाओं के बारे में पता है, यह मुझे समझ में नहीं आता है कि यह क्यों अनुमति नहीं देता है और क्यों यह आउटपुट अलग होना चाहता है कि यह संदर्भ या मूल्य प्रकार है जो मैन्युअल रूप से प्रकारों को लागू करने पर विचार करता है अपेक्षित परिणाम मिलेगा।

+0

'वर एक्स = समारोह लंबे प्रपत्र आप से पता चला है का उपयोग करें या विभिन्न बाधाओं के साथ दो विधियों की आवश्यकता होगी? .Invoke()' भी असफल हो जायेगी। 'x' शून्य हो सकता है या कुछ मूल्य हो सकता है। कंपाइलर यह नहीं जानता है। इसके अलावा उस कंपाइलर को यह नहीं पता कि 'टी' संदर्भ प्रकार है या नहीं। ध्यान दें कि 'शून्य' मूल्य प्रकारों पर मान्य नहीं है। उदाहरण के लिए आप 'int I = null' लिख नहीं सकते हैं। इस प्रकार आपको मिली त्रुटि। –

+1

संक्षेप में, 'Func ' का प्रकार? अगर 'टी' संदर्भ प्रकार है, और' टी? '' '' '' '' '' '' '' होना चाहिए। चूंकि .NET में जेनेरिकों में एक कार्यान्वयन है (जैसा कि सी ++ में टेम्पलेट्स के विपरीत है), यह आसानी से नहीं किया जा सकता है। सिद्धांत रूप में, संकलक चालाक कोड पीढ़ी द्वारा इस काम को करने के लिए पीछे की तरफ मोड़ सकता है। प्रैक्टिस में, सी # कंपाइलर का दर्शन पीछे की तरफ झुकना नहीं है, लेकिन चीजों को अस्वीकार करना है अगर उन्हें सीधे तौर पर नहीं किया जा सकता है। –

उत्तर

6

दुर्भाग्य से मेरा मानना ​​है कि आप एक बढ़त मामले मारा है

var g = new MyStruct(); 
var p = g ?? default(MyStruct); 

मैं संकलन त्रुटि मिलती है कंपाइलर का। ?. ऑपरेटर को कक्षाओं के लिए default(RetrunTypeOfRHS) और structs के लिए default(Nullable<RetrunTypeOfRHS>) वापस करने की आवश्यकता है। चूंकि आपने कक्षाओं या structs होने के लिए T को बाध्य नहीं किया है, यह नहीं बता सकता कि किसको बढ़ावा देना है।

कारण Action<T> काम करता है क्योंकि दाएं हाथ की वापसी का प्रकार void दोनों मामलों के लिए है, इसलिए यह तय करने की आवश्यकता नहीं है कि कौन सा पदोन्नति करना है।

आप पर T

public static T TestStruct<T>(Func<T> func) where T : struct 
    { 
     return func?.Invoke() ?? default(T); 
    } 

    public static T TestClass<T>(Func<T> func) where T : class 
    { 
     return func?.Invoke(); // ?? default(T); -- This part is unnecessary, ?. already 
               // returns default(T) for classes. 
    } 
+0

'डिफ़ॉल्ट (टी);' जब टी कक्षा हमेशा शून्य होती है तो इसे सुरक्षित रूप से हटाया जा सकता है। resharper हमेशा मुझे याद दिलाता है xD –

+1

@ एमकेज़ेमखरी रीफ्रेश, बहुत धीमी :) –

+0

कंपाइलर त्रुटि बहुत भयानक है। यह पता लगाने में कोई मदद नहीं करता कि क्या गलत है। +1 – InBetween

6

आप सामान्य समारोह पर एक बाधा स्थापित करना चाहिए:

public static T Test<T>(Func<T> func) where T: class 
{ 
    return func?.Invoke() ?? default(T); 
} 

क्योंकि एक struct खाली नहीं रह सकती और ?. एक संदर्भ प्रकार की आवश्यकता है।


जेरोन Mostert से टिप्पणी की वजह से

, मैं क्या हुड के नीचे होता है पर एक नज़र था। एक Func<T> एक प्रतिनिधि है जो एक संदर्भ प्रकार है। T पर किसी भी बाधा के बिना, कोड संकलित नहीं होगा। Error CS0023 Operator '?' cannot be applied to operand of type 'T'। जब आप बाधा where T: struct या where T: class जोड़ते हैं, तो अंतर्निहित कोड तैयार किया जाएगा।

कोड लिखा:

public static T TestStruct<T>(Func<T> func) where T : struct 
    { 
     return func?.Invoke() ?? default(T); 
    } 

    public static T TestClass<T>(Func<T> func) where T : class 
    { 
     return func?.Invoke() ?? default(T); 
    } 

कोड का उत्पादन किया और ILSpy साथ decompiled:

public static T TestStruct<T>(Func<T> func) where T : struct 
    { 
     return (func != null) ? func.Invoke() : default(T); 
    } 

    public static T TestClass<T>(Func<T> func) where T : class 
    { 
     T arg_27_0; 
     if ((arg_27_0 = ((func != null) ? func.Invoke() : default(T))) == null) 
     { 
      arg_27_0 = default(T); 
     } 
     return arg_27_0; 
    } 

आप देख सकते हैं, कोड का उत्पादन किया जब T एक struct है जब T एक वर्ग है से अलग है। इसलिए हमने ? त्रुटि तय की। लेकिन: ऑपरेटर समझ में नहीं आता है जब T एक संरचना है। मुझे लगता है कि संकलक को इस पर एक संकलन देना चाहिए। क्योंकि संरचना पर ?? का उपयोग करने की अनुमति नहीं है। #BeMoreStrict

उदाहरण के लिए:

अगर मैं लिखना:

Error CS0019 Operator '??' cannot be applied to operands of type 'MainPage.MyStruct' and 'MainPage.MyStruct'

+1

'डिफ़ॉल्ट (टी); 'हमेशा शून्य है इसलिए सुरक्षित रूप से हटाया जा सकता है। –

+1

"'? .' को एक संदर्भ प्रकार की आवश्यकता है "सबसे खराब और सबसे खराब पर भ्रामक है। 'Func ' * * एक संदर्भ प्रकार है, और 'func? Envoke()' कानूनी होगा यदि 'Func' प्रकार का था' Func '(परिणाम प्रकार' int? ') है। समस्या * विशेष रूप से * है कि मूल कोड में, एक सामान्य प्रकार 'टी' शामिल है जो या तो संदर्भ या मान प्रकार हो सकता है। –

+0

@JeroenMostert मैंने कुछ जानकारी जोड़ा। टिप्पणी के लिए धन्यवाद। –

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