2015-02-06 12 views
11

Tôi đã cố gắng để tạo ra một biểu thức switch với System.Linq.Expressions:Chuyển mà không trường hợp (nhưng với mặc định) trong 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); 

nhưng trong dòng cuối cùng tôi nhận được một ArgumentException:

Yêu cầu thu thập không trống. Tên thông số: trường hợp

Lý do ngoại lệ này là gì? Có thể là lỗi này trong số Expression.Switch(…)?

Trong C# một switch với phần "mặc định" chỉ là đúng:

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

UPD: Tôi đã nộp an issue đến repo CoreFX trên GitHub

+2

Mục tiêu của một công trình như vậy là gì? 'chuyển' với' mặc định' và không 'trường hợp' sẽ chỉ thực thi' mặc định' –

+0

Đối với tôi chuyển đổi mà không có trường hợp trông khá vô nghĩa, vì vậy tôi nghĩ ngoại lệ này là hợp lý. –

+0

Có, thông số C# cho biết khối chuyển đổi có số không hoặc nhiều phần chuyển đổi hơn; nhưng điều đó không có nghĩa là một biểu thức chuyển đổi phải liên kết với thông số C#. Vì bạn tạo biểu thức tại thời gian chạy, bạn có thể, như một giải pháp thay thế, chỉ cần thêm một 'Expression.SwitchCase' với giá trị là! = Giá trị chuyển đổi; hoặc thêm phần thân của trường hợp mặc định làm trường hợp chuyển đổi có giá trị = giá trị chuyển đổi. – sloth

Trả lời

6

Có không phải là một loại suy hoàn chỉnh giữa C# 's switchSwitchExpression. Theo một hướng khác, hãy xem xét mà bạn có thể có:

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 

Ở đây SwitchExpression trả về một giá trị mà là một cái gì đó switch không thể làm.

Vì vậy, cũng giống như việc có thể làm điều gì đó với SwitchExpression không có nghĩa là bạn có thể làm điều đó với một switch, vì vậy quá không có lý do gì để cho rằng việc có thể làm điều gì đó với một switch có nghĩa là bạn có thể làm điều đó với một SwitchExpression .

Điều đó nói rằng, tôi thấy không có lý do chính đáng tại sao SwitchExpression được đặt theo cách này, ngoại trừ có lẽ đơn giản hóa trường hợp biểu thức không có trường hợp . Điều đó nói rằng, tôi nghĩ rằng điều này có thể chỉ là một vấn đề của các biểu hiện thường được dự định có nhiều trường hợp, và đó là những gì nó được mã hóa để hỗ trợ.

Tôi sẽ cho phép các biểu thức ít trường hợp như vậy, bằng cách tạo một SwitchExpression trong đó giá trị mặc định cho loại switchValue có cùng nội dung với phần thân mặc định. Cách tiếp cận này có nghĩa là bất cứ điều gì sẽ gây ngạc nhiên bởi một SwitchExpression mà không có trường hợp nào vẫn phải đối phó, tránh các vấn đề tương thích ngược. Trường hợp không có mặc định hoặc được xử lý bằng cách tạo biểu thức noop không thực hiện gì, vì vậy trường hợp duy nhất hiện vẫn ném ArgumentException là nếu không có trường hợp không có mặc định loại này được đặt rõ ràng một cái gì đó khác hơn là void, trường hợp này không hợp lệ theo các quy tắc nhập rõ ràng phải được giữ nguyên.

[Cập nhật: Cách tiếp cận đó bị từ chối, nhưng a later pull-request đã được chấp nhận, vì vậy trường hợp ít hơn SwitchExpression s hiện được .NET Core cho phép, mặc dù nếu và khi được các phiên bản .NET khác chấp nhận.

Trong thời gian chờ đợi, hoặc nếu bạn sử dụng phiên bản khác.NET, bạn nhất bằng cách sử dụng một phương pháp helper như:

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. 
); 
} 

quá tải như:

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

Và như vậy sau đó có thể được thêm vào.

Kết quả này trong tông đơ Expression tổng thể hơn yêu cầu kéo của tôi, vì nó cắt ra hoàn toàn switch trong trường hợp không có trường hợp và chỉ trả về phần thân mặc định. Nếu bạn thực sự cần phải có một SwitchExpression thì bạn có thể tạo một phương thức trợ giúp tương tự theo cùng một logic như yêu cầu kéo đó trong việc tạo một SwitchCase mới và sau đó sử dụng nó.

Các vấn đề liên quan