Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 表达式树中的等式不';t不要使用正确的操作员过载_C#_Expression Trees_Roslyn - Fatal编程技术网

C# 表达式树中的等式不';t不要使用正确的操作员过载

C# 表达式树中的等式不';t不要使用正确的操作员过载,c#,expression-trees,roslyn,C#,Expression Trees,Roslyn,表达式树和运算符重载(特别是=和!=运算符)有一个奇怪的问题 我使用的是Marc Gravell的答案中的MemberwiseComparer,或多或少是一个 public static class MemberComparer { public static bool Equal<T>(T x, T y) { return EqualComparerCache<T>.Compare(x, y); } static cla

表达式树和运算符重载(特别是
=
!=
运算符)有一个奇怪的问题

我使用的是Marc Gravell的答案中的MemberwiseComparer,或多或少是一个

public static class MemberComparer
{
    public static bool Equal<T>(T x, T y)
    {
        return EqualComparerCache<T>.Compare(x, y);
    }

    static class EqualComparerCache<T>
    {
        internal static readonly Func<T, T, bool> Compare = (a, b) => true;

        static EqualComparerCache()
        {
            var members = typeof(T).GetTypeInfo().DeclaredProperties.Cast<MemberInfo>()
                .Concat(typeof(T).GetTypeInfo().DeclaredFields.Where(p => !p.IsStatic && p.IsPublic).Cast<MemberInfo>());
            var x = Expression.Parameter(typeof(T), "x");
            var y = Expression.Parameter(typeof(T), "y");

            Expression body = null;
            foreach (var member in members)
            {
                Expression memberEqual;
                if (member is FieldInfo)
                {
                    memberEqual = Expression.Equal(
                        Expression.Field(x, (FieldInfo)member),
                        Expression.Field(y, (FieldInfo)member));
                }
                else if (member is PropertyInfo)
                {
                    memberEqual = Expression.Equal(
                        Expression.Property(x, (PropertyInfo)member),
                        Expression.Property(y, (PropertyInfo)member));
                }
                else
                {
                    throw new NotSupportedException(member.GetType().GetTypeInfo().Name);
                }

                body = body == null ? memberEqual : Expression.AndAlso(body, memberEqual);
            }

            if (body != null)
            {
                var lambda = Expression.Lambda<Func<T, T, bool>>(body, x, y);
                Compare = lambda.Compile();
            }
        }
    }
}
但是当比较
Test2
时,它失败了:

var nestedTest1 = new Test2 { Test = new Test { Value = "TestValue"; } }
var nestedTest2 = new Test2 { Test = new Test { Value = "TestValue"; } }

Assert.True(nestedTest1==nestedTest2 ); // false
Assert.Equals(nestedTest1, nestedTest2 ); // false

// Second Test with referenced Test object
var test = new Test { Value = "TestValue"; }
var nestedTest1 = new Test2 { Test = test }
var nestedTest2 = new Test2 { Test = test }

Assert.True(nestedTest1==nestedTest2 ); // true
Assert.Equals(nestedTest1, nestedTest2 ); // true
=
运算符重写是为
Test2
类调用的,但不是为
Test
类调用的。当
nestedTest1
nestedTest2
引用相同的
Test
对象时,它工作。因此,在构建和编译表达式时,不会调用
==
重载

我找不到它会忽略它的原因。这是Roslyn的一些变化吗?没有人注意到,还是表达式树的生成有问题


当然,我可以重写表达式树生成来调用
.Equals
方法,但这会增加更多的复杂性(以及额外的空检查)。但实际问题是,为什么编译后的表达式树不使用
==
重载,以及如何使其工作

挖了一点之后,问题就来了。运算符
=
未在类
Test
中定义,但在
ValueType
中定义

如果你打电话

// this is used by Expression.Equal (it does not search for base type)

var m = typeof(Test).GetMethod("op_Equality", 
            BindingFlags.Static 
            | BindingFlags.Public | BindingFlags.NonPublic);

//m is null because op_Equality is not declared on "Test"

var m = typeof(ValueObject<>).GetMethod("op_Equality", 
            BindingFlags.Static 
            | BindingFlags.Public | BindingFlags.NonPublic);

// m is not null
//这由Expression.Equal使用(它不搜索基类型)
var m=typeof(Test).GetMethod(“op_相等”,
BindingFlags.Static
|BindingFlags.Public | BindingFlags.NonPublic);
//m为null,因为在“Test”上未声明op_相等
var m=typeof(ValueObject).GetMethod(“op_相等”,
BindingFlags.Static
|BindingFlags.Public | BindingFlags.NonPublic);
//m不是空的
这就是表达式不使用运算符相等方法的原因


Roslyn在编译时似乎确实使用了相等运算符,但表达式编译器不是Roslyn的一部分,这似乎是它在基类中不搜索方法的一个错误。

在深入挖掘之后,问题就来了。运算符
=
未在类
Test
中定义,但在
ValueType
中定义

如果你打电话

// this is used by Expression.Equal (it does not search for base type)

var m = typeof(Test).GetMethod("op_Equality", 
            BindingFlags.Static 
            | BindingFlags.Public | BindingFlags.NonPublic);

//m is null because op_Equality is not declared on "Test"

var m = typeof(ValueObject<>).GetMethod("op_Equality", 
            BindingFlags.Static 
            | BindingFlags.Public | BindingFlags.NonPublic);

// m is not null
//这由Expression.Equal使用(它不搜索基类型)
var m=typeof(Test).GetMethod(“op_相等”,
BindingFlags.Static
|BindingFlags.Public | BindingFlags.NonPublic);
//m为null,因为在“Test”上未声明op_相等
var m=typeof(ValueObject).GetMethod(“op_相等”,
BindingFlags.Static
|BindingFlags.Public | BindingFlags.NonPublic);
//m不是空的
这就是表达式不使用运算符相等方法的原因


Roslyn似乎在编译时使用了相等运算符,但表达式编译器不是Roslyn的一部分,这似乎是一个错误,它没有在基类中搜索方法。

我最终实现了一个方法,该方法搜索
op\u Equality
操作符覆盖方法,并将其作为第四个参数传递给
Expression.Equal

MethodInfo equalsOperator = FindMethod(memberType, "op_Equality", false);

equalityExpression = Expression.Equal(
    Expression.Property(left, memberInfo),
    Expression.Property(right, memberInfo),
    false,
    equalsOperator);

... 
private static MethodInfo FindMethod(Type type, string methodName, bool throwIfNotFound = true)
{
    TypeInfo typeInfo = type.GetTypeInfo();

    // TODO: Improve to search methods with a specific signature and parameters
    while (typeInfo != null)
    {
        IEnumerable<MethodInfo> methodInfo = typeInfo.GetDeclaredMethods(methodName);
        if (methodInfo.Any())
            return methodInfo.First();

        typeInfo = typeInfo.BaseType?.GetTypeInfo();
    }

    if (!throwIfNotFound)
        return null;

    throw new InvalidOperationException($"Type '{type.GetTypeInfo().FullName}' has no '{methodName}' method.");
}
MethodInfo equalsOperator=FindMethod(成员类型,“op_Equality”,false);
equalityExpression=Expression.Equal(
Expression.Property(左,memberInfo),
Expression.Property(权利、成员信息),
假,,
等式算子);
... 
私有静态MethodInfo FindMethod(类型类型,字符串methodName,bool throwIfNotFound=true)
{
TypeInfo-TypeInfo=type.GetTypeInfo();
//TODO:使用特定签名和参数改进搜索方法
while(typeInfo!=null)
{
IEnumerable methodInfo=typeInfo.GetDeclaredMethods(methodName);
if(methodInfo.Any())
返回methodInfo.First();
typeInfo=typeInfo.BaseType?.GetTypeInfo();
}
如果(!throwIfNotFound)
返回null;
抛出新的InvalidOperationException($“Type'{Type.GetTypeInfo().FullName}'没有'{methodName}'方法。”);
}

在我的简单场景中,使用第一个
op_Equality
就足够了,
ValueObject
类中不应该有多个
MemberComparer.Equal((T)this,other)
只被调用,当两个对象的类型相同时。

我最终实现了一个方法,该方法搜索
op\u Equality
运算符重写方法,并将其作为第四个参数传递给
表达式.Equal

MethodInfo equalsOperator = FindMethod(memberType, "op_Equality", false);

equalityExpression = Expression.Equal(
    Expression.Property(left, memberInfo),
    Expression.Property(right, memberInfo),
    false,
    equalsOperator);

... 
private static MethodInfo FindMethod(Type type, string methodName, bool throwIfNotFound = true)
{
    TypeInfo typeInfo = type.GetTypeInfo();

    // TODO: Improve to search methods with a specific signature and parameters
    while (typeInfo != null)
    {
        IEnumerable<MethodInfo> methodInfo = typeInfo.GetDeclaredMethods(methodName);
        if (methodInfo.Any())
            return methodInfo.First();

        typeInfo = typeInfo.BaseType?.GetTypeInfo();
    }

    if (!throwIfNotFound)
        return null;

    throw new InvalidOperationException($"Type '{type.GetTypeInfo().FullName}' has no '{methodName}' method.");
}
MethodInfo equalsOperator=FindMethod(成员类型,“op_Equality”,false);
equalityExpression=Expression.Equal(
Expression.Property(左,memberInfo),
Expression.Property(权利、成员信息),
假,,
等式算子);
... 
私有静态MethodInfo FindMethod(类型类型,字符串methodName,bool throwIfNotFound=true)
{
TypeInfo-TypeInfo=type.GetTypeInfo();
//TODO:使用特定签名和参数改进搜索方法
while(typeInfo!=null)
{
IEnumerable methodInfo=typeInfo.GetDeclaredMethods(methodName);
if(methodInfo.Any())
返回methodInfo.First();
typeInfo=typeInfo.BaseType?.GetTypeInfo();
}
如果(!throwIfNotFound)
返回null;
抛出新的InvalidOperationException($“Type'{Type.GetTypeInfo().FullName}'没有'{methodName}'方法。”);
}

在我的简单场景中,使用第一个
op_Equality
就足够了,
ValueObject
类中不应该有多个
MemberComparer.Equal((T)this,other)
仅在两个对象类型相同时才被调用。

起初我也认为,但是我不明白为什么
Assert.True(test1==test2)
确实有效,但通过表达式树的相同表达式(
memberEqual=expression.Equal(expression.Property(x,(PropertyInfo)成员)、expression.Property(y,(PropertyInfo)成员));
)无效。
MethodInfo equalsOperator = FindMethod(memberType, "op_Equality", false);

equalityExpression = Expression.Equal(
    Expression.Property(left, memberInfo),
    Expression.Property(right, memberInfo),
    false,
    equalsOperator);

... 
private static MethodInfo FindMethod(Type type, string methodName, bool throwIfNotFound = true)
{
    TypeInfo typeInfo = type.GetTypeInfo();

    // TODO: Improve to search methods with a specific signature and parameters
    while (typeInfo != null)
    {
        IEnumerable<MethodInfo> methodInfo = typeInfo.GetDeclaredMethods(methodName);
        if (methodInfo.Any())
            return methodInfo.First();

        typeInfo = typeInfo.BaseType?.GetTypeInfo();
    }

    if (!throwIfNotFound)
        return null;

    throw new InvalidOperationException($"Type '{type.GetTypeInfo().FullName}' has no '{methodName}' method.");
}