Сравните generi c два объекта - PullRequest
0 голосов
/ 26 марта 2020

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

        public static List<Variance> Compare<T>(this T original, T current)
    {
        var variances = new List<Variance>();
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var property in properties)
        {
            var v = new Variance
            {
                PropertyName = property.Name,
                Original = property.GetValue(original),
                Current = property.GetValue(current)
            };

            if (v.Original == null && v.Current == null)
            {
                continue;
            }
            if ((v.Original == null && v.Current != null) || (v.Original != null && v.Current == null))
            {
                variances.Add(v);
                continue;
            }
            if (!v.Original.Equals(v.Current))
            {
                variances.Add(v);
            }
        }
        return variances;
    }

, но если T - это список, который нужно заменить на SequenceEqual, и я не могу придумать, как преобразовать T в правильный тип списка для проверки SequenceEqual.

Ответы [ 2 ]

4 голосов
/ 26 марта 2020

Я думаю, что решение, которое вы хотите, это использовать динамическую c перегрузку.

Я сократил это решение от вашего, чтобы добраться до сути. Звонок на SwitchEquals() эквивалентен вашему v.Original.Equals(v.Current).

static bool SwitchEquals<T>(IEnumerable<T> listA, IEnumerable<T> listB)
{
    Console.WriteLine("Doing Sequential Equals");

    return true; // Do your sequential equal here
}

static bool SwitchEquals(object objA, object objB)
{
    Console.WriteLine("Doing equals");

    return objA.Equals(objB); // This is your original equals
}

static void Compare<T>(T original, T current)
{
    // Using dynamic means the decision between the tow SwitchEquals methods is made
    // At runtime, when the system knows if we have a collection
    if (SwitchEquals ((dynamic)original,(dynamic) current))
        Console.WriteLine("Match");
    else
        Console.WriteLine("No match");
}

static void Main(string[] args)
{
    Compare(4,5);

    Compare (new int[] { 4, 3 }, new int[] { 4, 4 });
}
3 голосов
/ 26 марта 2020

Хотя здесь невозможно использовать SequenceEqual, это будет включать либо dynamic, как в ответе Джаспера Кента, либо довольно много кода для размышления, я обрисую, что вам нужно сделать, если бы вы используя отражение:

  • проверьте, являются ли оба объекта IEnumerable<T>.
  • , получите Type обоих объектов, а затем получите параметр типа T как Type а также
  • получить метод SequenceEquals из Enumerable как MethodInfo. Это включает использование LINQ для поиска перегрузки с двумя параметрами.
  • вызов MakeGenericMethod с T.
  • Invoke MethodInfo

I Я не хотел бы читать или писать этот код ... Использование dynamic хорошо, я думаю, хотя некоторые люди имеют разные мнения ...

Так что вот третий способ: я предлагаю вам написать свой собственный SequenceEqual метод, который занимает IEnumerable с (не универсальная c версия):

private static bool SequenceEqual(IEnumerable first, IEnumerable second) {
    IEnumerator e1 = first.GetEnumerator();
    IEnumerator e2 = second.GetEnumerator();
    try {
        // adapted from https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,63644a881e976b52,references
        while (e1.MoveNext())
        {
            if (!(e2.MoveNext() && e1.Current.Equals(e2.Current))) return false;
        }
        if (e2.MoveNext()) return false;
        return true;
    } finally {
        if (e1 is IDisposable d1) {
            d1.Dispose();   
        }
        if (e2 is IDisposable d2) {
            d2.Dispose();
        }
    }
}

Тогда вы можете просто проверить, являются ли объекты IEnumerable:

if (v.Original is Enumerable e1 && 
    v.Current is Enumerable e2 &&
    !SequenceEqual(e1, e2))
{
    variances.Add(v);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...