I have a data stucture that is straightforward serializable (e.g. to XML, to JSON): There is a main class C1 and several other classes C2, C3, ..., Cn. All classes Ci have public properties which are either
- primitive types or
string IEnumerable<T>whereTis either primitive type orstringor any of the classesCj(wherej != i)Cj(wherej != i)
I want to define a generic equality comparer ValueEqualityComparer<T> which compares any of the classes Ci value-wise, i.e. in the following canonical way:
- if type
Tis primitive, useEquals(or==) - if type
TisIEnumerable<S>, useEnumerable.SequenceEqualon theSobjects with aValueEqualityComparer<S>as third argument - else, for each public property
PuseValueEqualityComparer<P>.Equalsand connect these results via&&.
I already learned that pattern matching like above is not possible directly, so I need reflection. But I am struggling with how to do that.
Here is what I have written so far:
public class ValueEqualityComparer<T> : IEqualityComparer<T>
{
public static readonly ValueEqualityComparer<T> Get = new ValueEqualityComparer<T>();
private static readonly Type _type;
private static readonly bool _isPrimitiveOrString;
private static readonly Type _enumerableElementType;
private static bool _isEnumerable => _enumerableElementType != null;
static ValueEqualityComparer()
{
_type = typeof(T);
_isPrimitiveOrString = IsPrimitiveOrString(_type);
_enumerableElementType = GetEnumerableElementTypeOrNull(_type);
}
private ValueEqualityComparer() {}
public bool Equals(T x, T y)
{
if (_isPrimitive)
return Equals(x, y);
if (_isEnumerable)
{
var comparerType = typeof(ValueEqualityComparer<>).MakeGenericType(new Type[] { _enumerableElementType });
var elementComparer = comparerType.GetMethod("Get").Invoke(null, 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
}
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();
}
}
Questions regarding the // not sure about this line: line:
- The goal is to call
Enumerable.SequenceEqual<S>(x, y, ValueEqualityComparer<S>.Get())whereSis the element type ofxandy(i.e.TisIEnumerable<S>). Is the line I wrote correct for that purpose? - How do I get the result (i.e.
trueorfalse) of that call?
Please don't fill in the // TODO section; I want to figure out as much as I can for myself.
Aucun commentaire:
Enregistrer un commentaire