C# 编写(非常)通用的相等比较器
我有一个可直接序列化的数据结构(例如到XML、到JSON):有一个主类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
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
调用saidEqual
方法
现在大家一起:
这将导致该类:
公共类值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 });