Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.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# 编写(非常)通用的相等比较器_C#_Generics_Reflection_Expression - Fatal编程技术网

C# 编写(非常)通用的相等比较器

C# 编写(非常)通用的相等比较器,c#,generics,reflection,expression,C#,Generics,Reflection,Expression,我有一个可直接序列化的数据结构(例如到XML、到JSON):有一个主类C1,还有几个其他类C2,C3,…,Cn。所有类Ci都具有public属性,这些属性是 基本类型或字符串 IEnumerable其中T是基元类型或string或任何类Cj(其中j!=i) Cj(其中j!=i) 没有循环引用 我想定义一个通用的相等比较器ValueEqualityComparer,它比较任何类Civalue-wise,即以以下标准方式: 如果类型T是基本类型,请使用等于(或==) 如果类型T是IEnumer

我有一个可直接序列化的数据结构(例如到XML、到JSON):有一个主类
C1
,还有几个其他类
C2
C3
,…,
Cn
。所有类
Ci
都具有
public
属性,这些属性是

  • 基本类型或
    字符串
  • IEnumerable
    其中
    T
    是基元类型或
    string
    或任何类
    Cj
    (其中
    j!=i
  • Cj
    (其中
    j!=i
没有循环引用

我想定义一个通用的相等比较器
ValueEqualityComparer
,它比较任何类
Ci
value-wise,即以以下标准方式:

  • 如果类型
    T
    是基本类型,请使用
    等于
    (或
    ==
  • 如果类型
    T
    IEnumerable
    ,则在
    S
    对象上使用
    ValueEqualityComparer
    作为第三个参数
  • 否则,对于每个公共属性
    P
    使用
    ValueEqualityComparer

    .Equals

    并通过
    和&
    连接这些结果
我已经了解到,像上面这样的模式匹配是不可能直接实现的,所以我需要反射。但我正在为如何做到这一点而挣扎

以下是我到目前为止所写的内容:

public class ValueEqualityComparer<T> : IEqualityComparer<T>
{
    public static readonly ValueEqualityComparer<T> Get = new ValueEqualityComparer<T>();
    private static readonly Type _type = typeof(T);
    private static readonly bool _isPrimitiveOrString = IsPrimitiveOrString(_type);
    private static readonly Type _enumerableElementType = GetEnumerableElementTypeOrNull(_type);
    private static bool _isEnumerable => _enumerableElementType != null;

    private ValueEqualityComparer() {}

    public bool Equals(T x, T y)
    {
        if (x == null || y == null)
        {
            if (x == null && y == null)
                return true;
            return false;
        }

        if (_isPrimitive)
            return x.Equals(y);

        if (_isEnumerable)
        {
            var comparerType = typeof(ValueEqualityComparer<>).MakeGenericType(new Type[] { _enumerableElementType });
            var elementComparer = comparerType.GetField("Get").GetValue(null);

            // not sure about this line:
            var result = Expression.Call(typeof(Enumerable), "SequenceEqual", new Type[] { _enumerableElementType },
                new Expression[] { Expression.Constant(x), Expression.Constant(y), Expression.Constant(elementComparer) });
        }

        // TODO: iterate public properties, use corresponding ValueEqualityComparers
    }

    public int GetHashCode(T obj)
    {
        // TODO
    }

    private static bool IsPrimitiveOrString(Type t) => t.IsPrimitive || t == typeof(string);

    // if we have e.g. IEnumerable<string>, it will return string
    private static Type GetEnumerableElementTypeOrNull(Type t)
    {
        Type enumerableType = t.GetInterfaces().Where(i => i.IsGenericType
            && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)).FirstOrDefault();
        return enumerableType?.GetGenericArguments().Single();
    }
}
公共类值EqualityComparer:IEqualityComparer
{
public static readonly ValueEqualityComparer Get=new ValueEqualityComparer();
私有静态只读类型_Type=typeof(T);
私有静态只读bool _isPrimitiveOrString=isPrimitiveOrString(_类型);
私有静态只读类型_enumerableElementType=GetEnumerableElementTypeOrNull(_类型);
私有静态bool _isEnumerable=>_enumerableElementType!=null;
私有值EqualityComparer(){}
公共布尔等于(TX,TY)
{
如果(x==null | | y==null)
{
如果(x==null&&y==null)
返回true;
返回false;
}
如果(_isPrimitive)
返回x等于(y);
如果(可计算)
{
var comparerType=typeof(ValueEqualityComparer).MakeGenericType(新类型[]{u enumerableElementType});
var elementComparer=comparerType.GetField(“Get”).GetValue(null);
//对这一行不太清楚:
var result=Expression.Call(typeof(Enumerable),“SequenceEqual”,新类型[]{u enumerableElementType},
新表达式[]{Expression.Constant(x)、Expression.Constant(y)、Expression.Constant(elementComparer)});
}
//TODO:迭代公共属性,使用相应的ValueEqualityComparers
}
公共int GetHashCode(T obj)
{
//待办事项
}
私有静态bool IsPrimitiveOrString(类型t)=>t.IsPrimitive | | t==typeof(字符串);
//如果我们有例如IEnumerable,它将返回字符串
私有静态类型GetEnumerableElementTypeOrNull(类型t)
{
类型enumerableType=t.GetInterfaces()。其中(i=>i.IsGenericType
&&i.GetGenericTypeDefinition()==typeof(IEnumerable)).FirstOrDefault();
返回enumerableType?.GetGenericArguments().Single();
}
}
关于
//行的问题:
行:

  • 目标是调用
    Enumerable.SequenceEqual(x,y,ValueEqualityComparer.Get)
    其中
    S
    x
    y
    的元素类型(即
    T
    IEnumerable
    )。我写的这句话是正确的吗
  • 如何获得该调用的结果(即
    true
    false
请不要填写
//TODO
部分;我想为自己找出尽可能多的答案


我不想比较序列化对象,因为它对调试没有多大帮助。毕竟,比较器是测试所必需的。

好的,所以它花了一些时间,但我想我得到了它,这里什么都没有


对于基本体和字符串 首先,如果是原语或字符串,只需调用
left.Equals(right)
(我决定调用参数
left
right
,而不是
x
y
,因为)


为平民 然后,如果它是一个可枚举的,事情就会变得更加复杂。首先,我们需要为新的
ValueEqualityComparer
创建一个泛型类型,然后我们得到它的构造函数并构造一个新的构造函数(这一步可能通过缓存(在
字典中)来增强)以前的相等比较器是按类型划分的,因此,如果一种类型中的许多类型被逐个比较,我们就不需要每次都创建一个新的比较器)。然后我们需要获得
SequenceEquals
方法,使其通用,并使用
left
right
elementComparer
调用它


每种类型 对于我们想要逐个属性比较的其他类型,我们首先需要获得给定类型的所有属性。然后我们需要迭代每个属性,从
left
right
获取属性的值,创建泛型
ValueEqualityComparer
,获取其
Equal
方法,最后用
leftProp
rightProp
调用said
Equal
方法


现在大家一起: 这将导致该类:

公共类值EqualityComparer:IEqualityComparer
{
私有只读类型_enumerableElementType;
私有只读布尔值是可计算的;
私有只读bool_isPrimitiveOrString;
公共价值平等比较人()
{
var类型=类型(T);
_isPrimitiveOrString=isPrimitiveOrString(类型);
//只检查它是否是可枚举的,
//如果当前类型为comp
// generic methods cannot be retrieved by the current GetMethod() api, this is a common workaround:
var sequenceEqualNongeneric = typeof(Enumerable).GetMethods().Where(
    m => m.Name == "SequenceEqual" && m.GetParameters().Length == 3).Single();
var sequenceEqualGeneric = sequenceEqualNongeneric.MakeGenericMethod(new Type[] { _enumerableElementType });
return (bool)sequenceEqualGeneric.Invoke(null, new object[] { x, y, elementComparer });