使用反射在字典上调用 Enumerable.Except (IEnumerable)
Invoke Enumerable.Except (IEnumerable) on dictionaries using reflection
我正在尝试使用反射来比较相同类型的对象的属性。
问题是引用类型 <T> == <T>
不会做所以我尝试使用反射来比较 IEnumerable 的值为此我尝试调用 Enumerable.Except(T)
它 适用于 List
但不适用于 Dictionaries
:
Unable to cast object of type
'd__571[System.Collections.Generic.KeyValuePair
2[System.String,System.String]]'
to type 'System.Collections.Generic.IEnumerable`1[System.Object]'.
此代码有问题:
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));
信息的完整代码
public static List<Variance> DetailedCompare<T>(this T val1, T val2)
{
List<Variance> variances = new List<Variance>();
PropertyInfo[] propertyInfo = val1.GetType().GetProperties();
foreach (PropertyInfo p in propertyInfo)
{
Variance v = new Variance();
v.Prop = p.Name;
v.valA = p.GetValue(val1);
v.valB = p.GetValue(val2);
switch (v.valA)
{
case null when v.valB == null:
continue;
case null:
variances.Add(v);
continue;
}
if (v.valA.Equals(v.valB)) continue;
if (typeof(IEnumerable).IsAssignableFrom(p.PropertyType))
{
//string
if (p.PropertyType == typeof(string))
{
variances.Add(v);
continue;
}
var args = p.PropertyType.GetGenericArguments();
MethodInfo exceptMethods = null;
if (args.Length == 2) //dictionaries
{
variances.Add(v); // add to difference while not able to compare
/*
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));*/
}
else if (args.Length == 1)//lists
{
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(p.PropertyType.GetGenericArguments().FirstOrDefault());
}
else//not
{
variances.Add(v);
}
if (exceptMethods != null)
{
try
{
var res1 = (IEnumerable<object>)exceptMethods.Invoke(v.valA, new[] { v.valA, v.valB });
var res2 = (IEnumerable<object>)exceptMethods.Invoke(v.valB, new[] { v.valB, v.valA });
if (res1.Any() != res2.Any()) variances.Add(v);
}
catch (Exception ex)
{
}
/* if (v.valA.Except(v.valB).Any() || v.valB.Except(v.valA).Any())
{
variances.Add(v);
}*/
}
}
}
return variances;
}
}
class Variance
{
public string Prop { get; set; }
public object valA { get; set; }
public object valB { get; set; }
}
我认为您可以考虑强制转换为通用 IEnumerable,然后使用 .OfType
重载函数将其装箱到对象。完整的代码如下所示:
void TestFunction()
{
var v1 = new { yes = "asdf", no = "as", ar = new List<int>() { 1, 2, 3 }, dict = new Dictionary<object, object>() { { 1, 1 }, { 2, 2 } } };
var v2 = new { yes = "asdf", no = "fd", ar = new List<int>() { 1, 2, 3 }, dict = new Dictionary<object, object>() { { 1, 1 }, { 2, 2 } } };
var differences = DetailedCompare(v1, v2);
}
public static List<Variance> DetailedCompare<T>(T val1, T val2)
{
List<Variance> variances = new List<Variance>();
PropertyInfo[] proppertyInfo = val1.GetType().GetProperties();
foreach (PropertyInfo p in proppertyInfo)
{
Variance v = new Variance();
v.Prop = p.Name;
v.valA = p.GetValue(val1);
v.valB = p.GetValue(val2);
switch (v.valA)
{
case null when v.valB == null:
continue;
case null:
variances.Add(v);
continue;
}
if (v.valA.Equals(v.valB)) continue;
if (typeof(IEnumerable).IsAssignableFrom(p.PropertyType))
{
//string
if (p.PropertyType == typeof(string))
{
variances.Add(v);
continue;
}
var args = p.PropertyType.GetGenericArguments();
MethodInfo exceptMethods = null;
if (args.Length == 2) //dictionaries
{
//variances.Add(v); // add to difference while not able to compare
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));
}
else if (args.Length == 1)//lists
{
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(p.PropertyType.GetGenericArguments().FirstOrDefault());
}
else//not
{
variances.Add(v);
}
if (exceptMethods != null)
{
try
{
var res1 = (IEnumerable)exceptMethods.Invoke(v.valA, new[] { v.valA, v.valB });
var res2 = (IEnumerable)exceptMethods.Invoke(v.valB, new[] { v.valB, v.valA });
// TODO: maybe implement better comparisson
if (res1.OfType<object>().Any() != res2.OfType<object>().Any()) variances.Add(v);
}
catch (Exception ex)
{
}
/* if (v.valA.Except(v.valB).Any() || v.valB.Except(v.valA).Any())
{
variances.Add(v);
}*/
}
}
}
return variances;
}
public class Variance
{
public string Prop { get; set; }
public object valA { get; set; }
public object valB { get; set; }
public override string ToString() => $" Property {Prop} is either {valA} resp. {valB}";
}
我正在尝试使用反射来比较相同类型的对象的属性。
问题是引用类型 <T> == <T>
不会做所以我尝试使用反射来比较 IEnumerable 的值为此我尝试调用 Enumerable.Except(T)
它 适用于 List
但不适用于 Dictionaries
:
Unable to cast object of type 'd__57
1[System.Collections.Generic.KeyValuePair
2[System.String,System.String]]' to type 'System.Collections.Generic.IEnumerable`1[System.Object]'.
此代码有问题:
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));
信息的完整代码
public static List<Variance> DetailedCompare<T>(this T val1, T val2)
{
List<Variance> variances = new List<Variance>();
PropertyInfo[] propertyInfo = val1.GetType().GetProperties();
foreach (PropertyInfo p in propertyInfo)
{
Variance v = new Variance();
v.Prop = p.Name;
v.valA = p.GetValue(val1);
v.valB = p.GetValue(val2);
switch (v.valA)
{
case null when v.valB == null:
continue;
case null:
variances.Add(v);
continue;
}
if (v.valA.Equals(v.valB)) continue;
if (typeof(IEnumerable).IsAssignableFrom(p.PropertyType))
{
//string
if (p.PropertyType == typeof(string))
{
variances.Add(v);
continue;
}
var args = p.PropertyType.GetGenericArguments();
MethodInfo exceptMethods = null;
if (args.Length == 2) //dictionaries
{
variances.Add(v); // add to difference while not able to compare
/*
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));*/
}
else if (args.Length == 1)//lists
{
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(p.PropertyType.GetGenericArguments().FirstOrDefault());
}
else//not
{
variances.Add(v);
}
if (exceptMethods != null)
{
try
{
var res1 = (IEnumerable<object>)exceptMethods.Invoke(v.valA, new[] { v.valA, v.valB });
var res2 = (IEnumerable<object>)exceptMethods.Invoke(v.valB, new[] { v.valB, v.valA });
if (res1.Any() != res2.Any()) variances.Add(v);
}
catch (Exception ex)
{
}
/* if (v.valA.Except(v.valB).Any() || v.valB.Except(v.valA).Any())
{
variances.Add(v);
}*/
}
}
}
return variances;
}
}
class Variance
{
public string Prop { get; set; }
public object valA { get; set; }
public object valB { get; set; }
}
我认为您可以考虑强制转换为通用 IEnumerable,然后使用 .OfType
重载函数将其装箱到对象。完整的代码如下所示:
void TestFunction()
{
var v1 = new { yes = "asdf", no = "as", ar = new List<int>() { 1, 2, 3 }, dict = new Dictionary<object, object>() { { 1, 1 }, { 2, 2 } } };
var v2 = new { yes = "asdf", no = "fd", ar = new List<int>() { 1, 2, 3 }, dict = new Dictionary<object, object>() { { 1, 1 }, { 2, 2 } } };
var differences = DetailedCompare(v1, v2);
}
public static List<Variance> DetailedCompare<T>(T val1, T val2)
{
List<Variance> variances = new List<Variance>();
PropertyInfo[] proppertyInfo = val1.GetType().GetProperties();
foreach (PropertyInfo p in proppertyInfo)
{
Variance v = new Variance();
v.Prop = p.Name;
v.valA = p.GetValue(val1);
v.valB = p.GetValue(val2);
switch (v.valA)
{
case null when v.valB == null:
continue;
case null:
variances.Add(v);
continue;
}
if (v.valA.Equals(v.valB)) continue;
if (typeof(IEnumerable).IsAssignableFrom(p.PropertyType))
{
//string
if (p.PropertyType == typeof(string))
{
variances.Add(v);
continue;
}
var args = p.PropertyType.GetGenericArguments();
MethodInfo exceptMethods = null;
if (args.Length == 2) //dictionaries
{
//variances.Add(v); // add to difference while not able to compare
var typeKeyValuePair = typeof(KeyValuePair<,>);
Type[] typeArgs = { args[0], args[1] };
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(typeKeyValuePair.MakeGenericType(typeArgs));
}
else if (args.Length == 1)//lists
{
exceptMethods = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(mi => mi.Name == "Except")
?.MakeGenericMethod(p.PropertyType.GetGenericArguments().FirstOrDefault());
}
else//not
{
variances.Add(v);
}
if (exceptMethods != null)
{
try
{
var res1 = (IEnumerable)exceptMethods.Invoke(v.valA, new[] { v.valA, v.valB });
var res2 = (IEnumerable)exceptMethods.Invoke(v.valB, new[] { v.valB, v.valA });
// TODO: maybe implement better comparisson
if (res1.OfType<object>().Any() != res2.OfType<object>().Any()) variances.Add(v);
}
catch (Exception ex)
{
}
/* if (v.valA.Except(v.valB).Any() || v.valB.Except(v.valA).Any())
{
variances.Add(v);
}*/
}
}
}
return variances;
}
public class Variance
{
public string Prop { get; set; }
public object valA { get; set; }
public object valB { get; set; }
public override string ToString() => $" Property {Prop} is either {valA} resp. {valB}";
}