使用VS2015 Update 1编译时,表达式破坏代码

21

在我的机器上安装了Visual Studio 2015 Update 1之后,我发现我的一些单元测试失败了。经过一番调查,我能够将问题简化为以下代码行:

Expression<Func<GameObject, bool>> expression = t => t.X == 0 && t.Y == 0 && t.GameObjectType == GameObjectType.WindMill;

当悬停在表达式变量上时,Visual Studio的不同版本显示结果不同:
VS 2015: VS 2015 VS 2015 Update 1: VS 2015 Update 1 比较枚举类型的逻辑(在ServiceStack.OrmLite代码的某个地方)现在行为不同,最终导致枚举无法被识别并导致单元测试失败。
我使用以下代码复现了问题:
class Program
{
    static void Main(string[] args)
    {
        var gameObjects = new List<GameObject> {
            new GameObject { X = 0, Y = 0, GameObjectType = GameObjectType.WindMill },
            new GameObject { X = 0, Y = 1, GameObjectType = GameObjectType.Pipe },
            new GameObject { X = 0, Y = 2, GameObjectType = GameObjectType.Factory }
        };

        var gameObjectsQueryable = gameObjects.AsQueryable();

        Expression<Func<GameObject, bool>> expression = t => t.X == 0 && t.Y == 0 && t.GameObjectType == GameObjectType.WindMill;

        var result = gameObjectsQueryable.Where(expression);

        var resultAsList = result.ToList();

        foreach (var item in resultAsList)
        {
            Console.WriteLine(item);
        }

        //Obtain the t.GameObjectType == GameObjectType.WindMill part
        var binaryExpression = expression.Body as BinaryExpression;
        var right = binaryExpression.Right;
        var binaryExpression2 = right as BinaryExpression;
        var right2 = binaryExpression2.Right;

        if (right2 is UnaryExpression)
        {
            Console.WriteLine("Found UnaryExpression (This happens when the solution is build with VS2015)...");

            var right2Unary = binaryExpression2.Right as UnaryExpression;
            var right2Constant = right2Unary.Operand as ConstantExpression;
            CheckIfConsantIsAsExpected(right2Constant);
        }
        else
        {
            Console.WriteLine("Found ConstantExpression (This happens when the solution is build with VS2015 Update 1)...");

            var right2Constant = binaryExpression2.Right as ConstantExpression;
            CheckIfConsantIsAsExpected(right2Constant);
        }

        Console.ReadKey();
    }

    public static void CheckIfConsantIsAsExpected(ConstantExpression expression)
    {
        if (expression.Value.Equals(GameObjectType.WindMill))
        {
            Console.WriteLine($"The value is the enum we expected :), : {expression.Value}");
        }
        else
        {
            Console.WriteLine($"The value is not the enum we expected :(, : {expression.Value}");
        }
    }
}

public class GameObject
{
    public int X { get; set; }
    public int Y { get; set; }
    public GameObjectType GameObjectType { get; set; }

    public override string ToString()
    {
        return $"{X},{Y}: {GameObjectType}";
    }
}

public enum GameObjectType
{
    WindMill = 100,
    Pipe = 200,
    Factory = 300
}

在VS 2015中,它将进入UnaryExpression路径,在VS 2015 Update 1中,它将进入ConstantExpression路径。如果您在VS 2015上编译解决方案,然后将已编译的.exe文件复制到VS 2015 Update 1系统中,它将与VS 2015版本相同运行(因此也是UnaryExpression路径)。这表明它与JIT无关,而是与构建相关。我的问题是这是否是有意的?(因为它可能会在简单重新编译解决方案时破坏现有代码)

2
这对我来说看起来是一个相当无害的优化。我不会期望它会破坏任何东西到不能工作的程度,尽管:确实,它将通过代码走不同的路径,因此检查哪个路径被采取的测试将会失败。然而,评估这个表达式的最终结果应该保持不变,并且将其转换为其他内容的最终结果应该产生等效的结果。 - Sergey Kalinichenko
1
代码出现问题是因为将枚举序列化为SQL查询。由于不知道正在处理的值实际上是枚举类型,因此会导致错误。 - Devedse
也许你有一个不同的程序集,其中包含被引用到你的表达式中的常量?你只是没有正确地重新构建它...这只是一个想法。你确定你的枚举在同一个程序集中吗? - George Lica
我有差不多相同的问题。这个问题在VS 2010和VS 2015中都会出现,但在LinqPad中却没有问题。 - Jerther
2
这是Roslyn引起的不幸事件。数以千计的错误,他们一直在修复它们,因此Update 1中的版本与RTM中的版本非常不同,很可能有自己的缺陷。只需将您自己的错误报告添加到堆栈中,然后单击[New Issue](https://github.com/dotnet/roslyn/issues)按钮即可。 - Hans Passant
你是否使用旧版的Visual Studio(例如2010)检查了你的代码?因为在2010年,Right也是常量表达式。 - Bagherani
1个回答

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接