简体   繁体   English

使用反射在字典上调用Enumerable.Except(IEnumerable)

[英]Invoke Enumerable.Except (IEnumerable) on dictionaries using reflection

I'm trying to use reflection to compare properties of objects that are the same type. 我正在尝试使用反射来比较相同类型的对象的属性。

The problem is that with reference types <T> == <T> won't do So I try to use reflection to compare values of IEnumerable for this I try to invoke Enumerable.Except(T) 问题在于引用类型<T> == <T>不会执行,所以我尝试使用反射来比较IEnumerable的值,为此我尝试调用Enumerable.Except(T)

It works on List but won't work for Dictionaries : 可以在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]'. 无法转换类型为'd__57 1[System.Collections.Generic.KeyValuePair 2 [System.String,System.String]]'的对象来键入'System.Collections.Generic.IEnumerable'1 [System.Object]'。

The issue is with this code : 问题在于此代码:

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));

Full code for the info 信息的完整代码

 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; }
}

I think you might consider casting to the generic IEnumerable, and then boxing it to objects with the .OfType overloading function. 我认为您可能会考虑将其强制转换为通用IEnumerable,然后使用.OfType重载函数将其装箱到对象。 The complete code would look like this: 完整的代码如下所示:

    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}";
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM