2015-02-06 16 views
11

मैं System.Linq.Expressions के साथ एक स्विच अभिव्यक्ति बनाने की कोशिश की है:स्विच

var value = Expression.Parameter(typeof(int)); 
var defaultBody = Expression.Constant(0); 
var cases1 = new[] { Expression.SwitchCase(Expression.Constant(1), Expression.Constant(1)), }; 
var cases2 = new SwitchCase[0]; 
var switch1 = Expression.Switch(value, defaultBody, cases1); 
var switch2 = Expression.Switch(value, defaultBody, cases2); 

लेकिन अंतिम पंक्ति में मैं एक ArgumentException मिलती है:

गैर-खाली संग्रह आवश्यक है। पैरामीटर नाम: मामले

इस अपवाद का कारण क्या है? Expression.Switch(…) में यह एक बग हो सकता है?

एक सी # में "डिफ़ॉल्ट" भाग के साथ एक स्विच केवल सही है:

switch(expr) { 
default: 
    return 0; 
}//switch 

युपीडी: मैं GitHub पर CoreFX रेपो को an issue प्रस्तुत की है

+2

ऐसे निर्माण का लक्ष्य क्या है? 'डिफ़ॉल्ट' के साथ 'स्विच'' और 'केस' सिर्फ 'डिफ़ॉल्ट' –

+0

निष्पादित करेगा मेरे लिए बिना किसी मामले के स्विच स्विच करना बहुत अर्थहीन दिखता है, इसलिए मुझे लगता है कि यह अपवाद उचित है। –

+0

हां, सी # स्पेक का कहना है कि स्विच-ब्लॉक में शून्य या अधिक स्विच-सेक्शन हैं; लेकिन इसका मतलब यह नहीं है कि एक स्विच अभिव्यक्ति को सी # spec तक सीमित करना है। चूंकि आप रनटाइम पर अभिव्यक्ति बनाते हैं, तो आप एक वर्कअराउंड के रूप में, एक 'Expression.SwitchCase' को उस मान के साथ जोड़ सकते हैं! = स्विच वैल्यू; या वैल्यू = स्विच वैल्यू के साथ स्विच केस के रूप में डिफॉल्ट केस का बॉडी जोड़ें। – sloth

उत्तर

6

वहाँ एक पूरा सादृश्य नहीं है सी # के switch और SwitchExpression के बीच। दूसरी दिशा में, पर विचार आप हो सकता है:

var value = Expression.Parameter(typeof(int)); 
var meth = Expression.Lambda<Func<int, string>>(
    Expression.Switch(
    value, 
    Expression.Call(value, typeof(object).GetMethod("ToString")), 
    Expression.SwitchCase(Expression.Constant("Zero"), Expression.Constant(0, typeof(int))), 
    Expression.SwitchCase(Expression.Constant("One"), Expression.Constant(1, typeof(int)))), 
    value 
).Compile(); 
Console.WriteLine(meth(0)); // Zero 
Console.WriteLine(meth(1)); // One 
Console.WriteLine(meth(2)); // 2 

यहाँ SwitchExpression एक मूल्य जो कुछ switch ऐसा नहीं कर सकते है देता है।

तो, सिर्फ SwitchExpression साथ कुछ करने के लिए इसका मतलब यह नहीं कि आप एक switch साथ यह कर सकते हैं, तो भी वहाँ ग्रहण करने के लिए एक switch साथ कुछ करने के लिए सक्षम किया जा रहा मतलब है कि आप एक SwitchExpression साथ ऐसा कर सकते हैं कोई कारण नहीं है सक्षम होने के रूप में ।

कहा, मुझे कोई अच्छा कारण SwitchExpression सिवाय शायद इस तरह से स्थापित किया गया था, कि यह इस मामले में जहां एक अभिव्यक्ति नहीं मामलों और कोई डिफ़ॉल्ट शरीर है सरल देखते हैं। उस ने कहा, मुझे लगता है कि यह आमतौर पर अभिव्यक्ति का मामला था, जिसका उद्देश्य आम तौर पर कई मामलों के लिए होता था, और यही वह था जिसे समर्थन देने के लिए कोड किया गया था।

मैं submitted a pull-request to .NET Core कि एक SwitchExpression जहां switchValue के प्रकार के लिए डिफ़ॉल्ट मान डिफ़ॉल्ट शरीर के रूप में ही शरीर है का निर्माण करके इस तरह के मामले कम भाव की अनुमति होगी, न किए हों। इस दृष्टिकोण का मतलब है कि SwitchExpression द्वारा किसी भी मामले को आश्चर्यचकित किया जाएगा, बिना किसी मामले को पीछे-संगतता समस्याओं से परहेज करना चाहिए। कोई डिफ़ॉल्ट नहीं होने का मामला या तो नोप अभिव्यक्ति बनाकर निपटाया जाता है जो कुछ भी नहीं करता है, इसलिए एकमात्र मामला जो अब भी ArgumentException फेंकता है, यदि कोई मामला और कोई डिफ़ॉल्ट और प्रकार स्पष्ट रूप से सेट है void के अलावा कुछ और, यह मामला टाइपिंग नियमों के तहत अमान्य है जिसे स्पष्ट रूप से अभी भी रखा जाना चाहिए।

[अपडेट: यह दृष्टिकोण को अस्वीकार कर दिया गया था, लेकिन a later pull-request स्वीकार कर लिया गया है, तो केस-कम SwitchExpression रों अब हालांकि अगर और जब वह यह है कि एक और बात नेट के अन्य संस्करणों ने भी अपनाया है, नेट कोर द्वारा अनुमति दी जाती है।]

इस बीच, या यदि आप किसी अन्य संस्करण का उपयोग करते हैं।

public static Expression SwitchOrDefault(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable<SwitchCase> cases) 
{ 
    if (cases != null) 
    { 
    // It's possible that cases is a type that can only be enumerated once. 
    // so we check for the most obvious condition where that isn't true 
    // and otherwise create a ReadOnlyCollection. ReadOnlyCollection is 
    // chosen because it's the most efficient within Switch itself. 
    if (!(cases is ICollection<SwitchCase>)) 
     cases = new ReadOnlyCollection<SwitchCase>(cases); 
    if (cases.Any()) 
     return Switch(type, switchValue, defaultBody, comparison, cases); 
    } 
    return Expression.Block(
    switchValue, // include in case of side-effects. 
    defaultBody != null ? defaultBody : Expression.Empty() // replace null with a noop expression. 
); 
} 

Overloads की तरह:

public static Expression SwitchOrDefault(Expression switchValue, Expression defaultBody, params SwitchCase[] cases) 
{ 
    return SwitchOrDefault(switchValue, defaultBody, null, (IEnumerable<SwitchCase>)cases); 
} 

और इसी तरह तो जोड़ा जा सकता है नेट, आप सबसे अच्छा बंद की तरह एक सहायक विधि का उपयोग कर रहे हैं।

यह मेरे पुल अनुरोध के मुकाबले एक ट्रिमर Expression में परिणाम देता है, क्योंकि यह switch पूरी तरह से नो-केस केस में कट करता है और केवल डिफ़ॉल्ट निकाय देता है। यदि आप वास्तव में को SwitchExpression रखने की आवश्यकता है तो आप एक समान सहायक विधि बना सकते हैं जो उसी तर्क का अनुसरण करता है क्योंकि पुल-अनुरोध नया SwitchCase बनाने में और उसके बाद इसका उपयोग करता है।