Вызов Enumerable.Except (IEnumerable) для словарей с использованием отражения

Я пытаюсь использовать отражение для сравнения свойств объектов одного типа.

Проблема в том, что со ссылочными типами <T> == <T> не получится. Поэтому я пытаюсь использовать отражение для сравнения значений IEnumerable, для этого я пытаюсь вызвать Enumerable.Except(T)

Это работает на List, но не будет работать для Dictionaries:

Unable to cast object of type 'd__571[System.Collections.Generic.KeyValuePair2[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; }
}

Стоит попытаться переосмыслить это. Не могли бы вы реализовать для типов T какой-то интерфейс или даже просто переопределить Object.Equals для них.

Andy Skirrow 02.05.2019 18:02

Я попробовал код, но, похоже, он тоже не работает с типизированными списками, например, для List<int> типов. Не могли бы вы расширить свой пример для короткого вызова функции на DetailedCompare?

user287107 02.05.2019 19:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, вы могли бы подумать о приведении к общему 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}";
    }

Другие вопросы по теме