问题 这是一个ExpressionTrees错误吗?


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 对于此次转化:

Single -> UInt32 Single -> UInt64

Double -> UInt32 Double -> UInt64

看起来很奇怪,不是吗?

<===添加了一些我的研究===>

我看了编译 DynamicMethod 使用MSIL代码 DynamicMethod Visualizer 和一些反思黑客得到 DynamicMethod 来自编译 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);

我得到的是这个MSIL代码:

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

并且相同的C#编译方法有这个主体:

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

现在有人看到ExpressionTrees编译这个转换的无效代码吗?


12550
2017-11-04 16:17


起源



答案:


这是 明确地 一个bug,它在今天的C#4.0版本中重现。感谢您提请我们注意。这个问题会有好处  在最终发布之前制作固定条;在这个最后阶段,我们只采取非常高优先级的修复,我们有信心不会破坏发布的稳定性。更有可能的是,修复将使其成为未来的服务版本;但当然,没有承诺。


11
2017-11-04 21:31





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

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

首先做错事时,你获得不同的价值观真的很重要吗?如果修改代码以使用检查转换(x => checked((uint)x)),则在两种情况下都会得到相同的结果 - 运行时异常。


2
2017-11-04 18:08



谢谢你的答案,但是,重要的是溢出行为是不同的!编译方法与普通代码有不同行为的原因是什么? - ControlFlow
除了之外,C#不使用其他技术 conv.u4 用于将浮点值转换为无符号整数的MSIL操作码=) - ControlFlow


答案:


这是 明确地 一个bug,它在今天的C#4.0版本中重现。感谢您提请我们注意。这个问题会有好处  在最终发布之前制作固定条;在这个最后阶段,我们只采取非常高优先级的修复,我们有信心不会破坏发布的稳定性。更有可能的是,修复将使其成为未来的服务版本;但当然,没有承诺。


11
2017-11-04 21:31





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

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

首先做错事时,你获得不同的价值观真的很重要吗?如果修改代码以使用检查转换(x => checked((uint)x)),则在两种情况下都会得到相同的结果 - 运行时异常。


2
2017-11-04 18:08



谢谢你的答案,但是,重要的是溢出行为是不同的!编译方法与普通代码有不同行为的原因是什么? - ControlFlow
除了之外,C#不使用其他技术 conv.u4 用于将浮点值转换为无符号整数的MSIL操作码=) - ControlFlow


我不确定这是不是一个错误,但我可以指出差异的方向:

这两种方法的构建方式不同。编译为converter1的表达式具有类型的目标方法 DynamicMethod的。分配给converter2的lambda方法的目标方法是 RuntimeMethodInfo

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

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

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
2017-11-04 19:38



不,你错了,.NET使用相同的JIT编译器来发出用于编译静态程序集或DynamicMethods的本机代码。 - ControlFlow