2009-11-04 132 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 
    } 
} 

同不同的行为可以为这个转换编译Expression.Convert时,可以成立:这是一个ExpressionTrees错误?

Single -> UInt32 Single -> UInt64

Double -> UInt32 Double -> UInt64

看起来很奇怪,不是吗?

< ===添加使用DynamicMethod Visualizer和一些反映我的一些研究===>

我看了一下编译DynamicMethod MSIL代码破解摆脱编译Expression<TDelegate>DynamicMethod

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

而我得到的是这样的MSIL代码:

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

而且等于C#-compiled方法有此体:

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

不要任何人现在看到ExpressionTrees编译无效代码,这种转换?

回答

11

这是显然一个错误,它在当今构建的C#4.0再现。感谢您引起我们的注意。赔率是好的,这个问题将而不是使最终发布前的固定栏;在这个晚期阶段,我们只采取非常高优先级的修复方案,我们有信心不会破坏版本的稳定。更有可能的是,修补程序将使其成为未来的服务版本;但当然,没有承诺。

2

我没有在这里看到的一个问题。在理想的情况下,你应该在两种情况下都会遇到编译错误。因为结果实际上是无声的溢出。 例如,只需以下不会编译:

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

是否真的重要,你在第一时间做了错误的事情时,你得到的值不同?如果您修改代码以使用检查转换(X =>检查((UINT)x)的),你会得到同样的结果在这两种情况下 - 一个运行时异常。

+1

Thx为您的答案,但是,重要的是,溢出行为是不同的!编译方法与纯代码有不同行为的原因是什么? – ControlFlow 2009-11-04 18:20:06

+0

C#不使用'conv.u4' MSIL操作码来转换浮点值为无符号整数的其他技术=) – ControlFlow 2009-11-04 20:19:59

0

我不知道这是否是一个错误或没有,但我可以指向差异的方向:

这两种方法不同的建造。编译到converter1的表达式有一个DynamicMethod类型的目标方法。分配给converter2拉姆达方法具有RuntimeMethodInfo目标方法。

两个JIT编译的,而是通过不同的机制。正如我所说,不能说出为什么他们有不同的行为,但这可能是导致差异的原因。

编辑这是它编译的内容(使用反射器的代码)。

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

它为某种意义上的结果是不同的。

+0

不,你错了,.NET使用相同的JIT编译器来发出本地代码来编译静态程序集或DynamicMethods无论什么。 – ControlFlow 2009-11-04 20:15:55