2009-11-04 51 views
11
using System; 
using System.Linq.Expressions; 

class Program 
{ 
    static void Main() 
    { 
    Expression<Func<float, uint>> expr = x => (uint) x; 

    Func<float,uint> converter1 = expr.Compile(); 
    Func<float,uint> converter2 = x => (uint) x; 

    var aa = converter1(float.MaxValue); // == 2147483648 
    var bb = converter2(float.MaxValue); // == 0 
    } 
} 

hành vi tương tự khác nhau có thể được thành lập khi biên dịch Expression.Convert cho các chuyển đổi này:Đây có phải là lỗi ExpressionTrees không?

Single -> UInt32 Single -> UInt64

Double -> UInt32 Double -> UInt64

Trông lạ, phải không?

< === thêm một số nghiên cứu của tôi ===>

Tôi nhìn vào biên soạn DynamicMethod mã MSIL sử dụng DynamicMethod Visualizer và một số phản ánh hack để có được DynamicMethod từ biên soạn Expression<TDelegate>:

Expression<Func<float, uint>> expr = x => (uint) x; 

Func<float,uint> converter1 = expr.Compile(); 
Func<float,uint> converter2 = x => (uint) x; 

// get RTDynamicMethod - compiled MethodInfo 
var rtMethodInfo = converter1.Method.GetType(); 

// get the field with the reference 
var ownerField = rtMethodInfo.GetField(
    "m_owner", BindingFlags.NonPublic | BindingFlags.Instance); 

// get the reference to the original DynamicMethod 
var dynMethod = (DynamicMethod) ownerField.GetValue(converter1.Method); 

// show me the MSIL 
DynamicMethodVisualizer.Visualizer.Show(dynMethod); 

Và những gì tôi nhận được là mã MSIL này:

IL_0000: ldarg.1 
IL_0001: conv.i4 
IL_0002: ret 

Và C# phương pháp -compiled bình đẳng có thân hình này:

IL_0000: ldarg.0 
IL_0001: conv.u4 
IL_0002: ret 

Đừng ai nhìn thấy bây giờ mà ExpressionTrees không biên dịch mã hợp lệ cho chuyển đổi này?

Trả lời

11

Đây là rõ ràng lỗi và tái tạo trong phiên bản C# 4.0 ngày nay. Cảm ơn bạn đã lưu ý đến chúng tôi.Tỷ lệ cược tốt là vấn đề này sẽ không làm cho thanh để sửa chữa trước khi phát hành cuối cùng; ở giai đoạn cuối này, chúng tôi chỉ thực hiện các bản sửa lỗi có mức độ ưu tiên rất cao mà chúng tôi tin tưởng sẽ không làm mất ổn định bản phát hành. Có nhiều khả năng là sửa chữa sẽ biến nó thành bản phát hành dịch vụ trong tương lai; nhưng tất nhiên, không có lời hứa.

2

Tôi không thấy sự cố ở đây. Trong trường hợp lý tưởng, bạn sẽ nhận được một lỗi biên dịch trong cả hai tình huống. Do đó, kết quả thực sự là một luồng tràn im lặng. Ví dụ, sau đây chỉ đơn giản là sẽ không biên dịch:

var test = (uint)(float.MaxValue); 

Liệu nó thực sự quan trọng mà bạn nhận được giá trị khác nhau khi làm một điều sai ở nơi đầu tiên? Nếu bạn sửa đổi mã của bạn để sử dụng chuyển đổi đã chọn (x => checked ((uint) x)), bạn sẽ nhận được kết quả tương tự trong cả hai trường hợp - một ngoại lệ thời gian chạy.

+1

Thx cho câu trả lời của bạn, nhưng có, điều quan trọng là hành vi tràn là khác nhau! Lý do cho phương pháp được biên dịch có hành vi khác với mã đơn giản là gì? – ControlFlow

+0

C# không sử dụng các kỹ thuật khác ngoại trừ mã vạch MSIL 'conv.u4' để chuyển đổi các giá trị dấu phẩy động thành số nguyên không dấu =) – ControlFlow

0

Tôi không chắc đó có phải là lỗi hay không, nhưng tôi có thể trỏ đến hướng khác nhau:

Hai phương pháp được tạo khác nhau. Biểu thức được biên dịch thành converter1 có phương thức đích là loại DynamicMethod. Phương thức lambda được gán cho converter2 có phương pháp đích là RuntimeMethodInfo.

Cả JIT được biên dịch nhưng thông qua cơ chế khác nhau. Như tôi đã nói, không thể nói tại sao họ có hành vi khác nhau nhưng điều này có lẽ là nguyên nhân dẫn đến sự khác biệt.

Chỉnh sửa Đây là những gì nó biên dịch thành (mã sử dụng Trình phản xạ).

ParameterExpression CS$0$0000; 
Func<float, uint> converter1 = Expression.Lambda<Func<float, uint>>(Expression.Convert(CS$0$0000 = Expression.Parameter(typeof(float), "x"), typeof(uint)), new ParameterExpression[] { CS$0$0000 }).Compile(); 
Func<float, uint> converter2 = delegate (float x) { return (uint) x; }; 
uint aa = converter1(float.MaxValue); 
uint bb = converter2(float.MaxValue); 

Có một số lý do tại sao kết quả lại khác.

+0

Không, bạn sai, .NET sử dụng cùng trình biên dịch JIT để phát ra mã gốc để biên dịch các cụm tĩnh hoặc DynamicMethods bất cứ điều gì. – ControlFlow

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