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>
whereT
is either primitive type orstring
or 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
T
is primitive, useEquals
(or==
) - if type
T
isIEnumerable<S>
, useEnumerable.SequenceEqual
on theS
objects with aValueEqualityComparer<S>
as third argument - else, for each public property
P
useValueEqualityComparer<P>.Equals
and 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())
whereS
is the element type ofx
andy
(i.e.T
isIEnumerable<S>
). Is the line I wrote correct for that purpose? - How do I get the result (i.e.
true
orfalse
) 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