Подтвердить что ты не робот

Сравните два списка различий

Мне хотелось бы получить некоторые отзывы о том, как лучше всего написать общую функцию, которая позволит сравнивать два списка. Списки содержат объекты класса, и мы хотели бы перебирать один список, ища тот же элемент во втором списке и сообщать о любых различиях.

У нас уже есть метод для сравнения классов, поэтому нам нужна обратная связь о том, как мы можем подать метод (показано ниже) из двух списков.

Например, скажем, у нас есть простой класс "Сотрудник", который имеет три свойства: имя, идентификатор, отдел. Мы хотим сообщить о различиях между списком и другим списком.

Примечание:
Оба списка всегда будут содержать одинаковое количество элементов.

Как уже упоминалось выше, у нас есть общий метод, который мы используем для сравнения двух классов, как мы можем включить этот метод для обслуживания списков, т.е. из другого метода, перебирать список и комбинировать классы с общим методом... но как мы можем найти эквивалентный класс во втором списке, чтобы перейти к следующему методу:

public static string CompareTwoClass_ReturnDifferences<T1, T2>(T1 Orig, T2 Dest)
    where T1 : class
    where T2 : class
{
    // Instantiate if necessary
    if (Dest == null) throw new ArgumentNullException("Dest", "Destination class must first be instantiated.");

    var Differences = CoreFormat.StringNoCharacters;

    // Loop through each property in the destination  
    foreach (var DestProp in Dest.GetType().GetProperties())
    {
        // Find the matching property in the Orig class and compare
        foreach (var OrigProp in Orig.GetType().GetProperties())
        {

            if (OrigProp.Name != DestProp.Name || OrigProp.PropertyType != DestProp.PropertyType) continue;
            if (OrigProp.GetValue(Orig, null).ToString() != DestProp.GetValue(Dest, null).ToString())
                Differences = Differences == CoreFormat.StringNoCharacters 
                    ? string.Format("{0}: {1} -> {2}", OrigProp.Name,
                                                       OrigProp.GetValue(Orig, null),
                                                       DestProp.GetValue(Dest, null)) 
                    : string.Format("{0} {1}{2}: {3} -> {4}", Differences,
                                                              Environment.NewLine,
                                                              OrigProp.Name,
                                                              OrigProp.GetValue(Orig, null),
                                                              DestProp.GetValue(Dest, null));
        }
    }
    return Differences;
}

Любые предложения или идеи оценены?

Изменить: Ориентация .NET 2.0, поэтому LINQ не может быть и речи.

4b9b3361

Ответ 1

.... но как найти эквивалентный класс во втором списке, чтобы перейти к методу ниже;

Это ваша фактическая проблема; вы должны иметь по крайней мере одно неизменяемое свойство, идентификатор или что-то в этом роде, чтобы идентифицировать соответствующие объекты в обоих списках. Если у вас нет такого имущества, вы не можете решить проблему без ошибок. Вы можете просто попытаться угадать соответствующие объекты, выполнив минимальные или логические изменения.

Если у вас есть такое свойство, решение становится очень простым.

Enumerable.Join(
   listA, listB,
   a => a.Id, b => b.Id,
   (a, b) => CompareTwoClass_ReturnDifferences(a, b))

благодаря вам и danbruc и Noldorin за ваши отзывы. оба списка будут одинаковыми длины и в том же порядке. поэтому метод выше близок, но вы можете изменить это метод для передачи enum.Current к методу, указанному выше?

Теперь я смущен... в чем проблема? Почему не только следующее?

for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
    yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}

Вызов Math.Min() может даже быть опущен, если гарантирована равная длина.


Реализация Noldorin, конечно, умнее из-за делегирования и использования счетчиков вместо использования ICollection.

Ответ 2

Это решение создает список результатов, содержащий все отличия от обоих входных списков. Вы можете сравнить свои объекты по любому свойству, в моем примере это идентификатор. Единственное ограничение состоит в том, что списки должны быть одного типа:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));

Ответ 3

Я думаю, вы ищете такой метод:

public static IEnumerable<TResult> CompareSequences<T1, T2, TResult>(IEnumerable<T1> seq1,
    IEnumerable<T2> seq2, Func<T1, T2, TResult> comparer)
{
    var enum1 = seq1.GetEnumerator();
    var enum2 = seq2.GetEnumerator();

    while (enum1.MoveNext() && enum2.MoveNext())
    {
        yield return comparer(enum1.Current, enum2.Current);
    }
}

Это не проверено, но тем не менее должно выполнять эту работу. Обратите внимание, что особенно полезным в этом методе является то, что он является полным, т.е. Он может принимать две последовательности произвольных (и разных) типов и возвращать объекты любого типа.

Это решение, конечно, предполагает, что вы хотите сравнить n-й элемент seq1 с n-м элементом в seq2. Если вы хотите соответствовать элементам в двух последовательностях, основанным на определенном свойстве/сравнении, тогда вы захотите выполнить какой-то join (как было предложено danbruc с помощью Enumerable.Join. Сообщите мне, не является ли это ни одним из этих подходов, что я потом, и, возможно, я могу предложить что-то еще.

Edit: Вот пример того, как вы можете использовать метод CompareSequences с функцией сопоставления, которую вы изначально разместили.

// Prints out to the console all the results returned by the comparer function (CompareTwoClass_ReturnDifferences in this case).
var results = CompareSequences(list1, list2, CompareTwoClass_ReturnDifferences);
int index;    

foreach(var element in results)
{
    Console.WriteLine("{0:#000} {1}", index++, element.ToString());
}

Ответ 4

Этот подход от Microsoft работает очень хорошо и предоставляет возможность сравнить один список с другим и переключить их, чтобы получить разницу в каждом. Если вы сравниваете классы, просто добавьте свои объекты в два отдельных списка и затем выполните сравнение.

http://msdn.microsoft.com/en-us/library/bb397894.aspx

Ответ 5

Я надеюсь, что правильно понимаю ваш вопрос, но вы можете сделать это очень быстро с Linq. Я предполагаю, что универсально вы всегда будете иметь свойство Id. Просто создайте интерфейс, чтобы обеспечить это.

Если вы определяете, что объект имеет одинаковые изменения от класса к классу, я бы рекомендовал передать делегат, который возвращает true, если два объекта имеют одинаковый постоянный идентификатор.

Вот как это сделать в Linq:

List<Employee> listA = new List<Employee>();
        List<Employee> listB = new List<Employee>();

        listA.Add(new Employee() { Id = 1, Name = "Bill" });
        listA.Add(new Employee() { Id = 2, Name = "Ted" });

        listB.Add(new Employee() { Id = 1, Name = "Bill Sr." });
        listB.Add(new Employee() { Id = 3, Name = "Jim" });

        var identicalQuery = from employeeA in listA
                             join employeeB in listB on employeeA.Id equals employeeB.Id
                             select new { EmployeeA = employeeA, EmployeeB = employeeB };

        foreach (var queryResult in identicalQuery)
        {
            Console.WriteLine(queryResult.EmployeeA.Name);
            Console.WriteLine(queryResult.EmployeeB.Name);
        }