c # сравнить данные в двух объектных моделях - PullRequest
8 голосов
/ 06 января 2011

У меня есть диалог, когда он появляется, он заполняется данными в объектной модели. На этом этапе данные копируются и сохраняются в «резервной» объектной модели. Когда пользователь закончил вносить свои изменения и нажал «ОК», чтобы закрыть диалоговое окно, мне нужен быстрый способ сравнения модели объекта резервного копирования с действующей - если что-то изменилось, я могу создать для пользователя новое состояние отмены.

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

Если бы я сериализовал обе объектные модели, и они были идентичны, но сохранены в разных местах памяти, они были бы равны? Существует ли какой-то простой способ сравнения двух сериализованных объектных моделей?

Ответы [ 3 ]

11 голосов
/ 17 января 2011

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

BinaryFormatter formatter = new BinaryFormatter();
m_backupStream = new MemoryStream();
formatter.Serialize(m_backupStream,m_objectModel);

Затем, если пользователь добавляет к объектной модели, используя доступные элементы управления (или нет).Когда диалоговое окно закрывается, вы можете сравнить исходную сериализацию с новой - это для меня то, как я могу решить, требуется ли состояние отмены.

BinaryFormatter formatter = new BinaryFormatter();
MemoryStream liveStream = new MemoryStream();
formatter.Serialize(liveStream,m_objectModel);
byte[] streamOneBytes = liveStream.ToArray();
byte[] streamTwoBytes = m_backupStream.ToArray();
if(!CompareArrays(streamOneBytes, streamTwoBytes))
    AddUndoState();

И функция сравнения массивов, если она кому-нибудь нужна- Возможно, не лучший способ сравнения двух массивов.

private bool CompareArrays(byte[] a, byte[] b)
{
    if (a.Length != b.Length)
       return false;

    for (int i = 0; i < a.Length;i++)
    {
       if (a[i] != b[i])
        return false;
    }
    return true;
}
6 голосов
/ 06 января 2011

Я бы сказал, что лучший способ - это реализовать операторы равенства во всех классах вашей модели (что, как правило, хорошая идея, если вы собираетесь проводить сравнения).

class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
    public ICollection<Chapter> Chapters { get; set; }

    public bool Equals(Book other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.Title, Title) && Equals(other.Author, Author) && Equals(other.Chapters, Chapters);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (Book)) return false;
        return Equals((Book) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result = (Title != null ? Title.GetHashCode() : 0);
            result = (result*397) ^ (Author != null ? Author.GetHashCode() : 0);
            result = (result*397) ^ (Chapters != null ? Chapters.GetHashCode() : 0);
            return result;
        }
    }
}

ЭтоФрагмент автоматически генерируется ReSharper, но вы можете использовать это в качестве основы.По сути, вам придется расширять не переопределенный метод Equals с помощью собственной логики сравнения.

Например, вы можете использовать SequenceEquals из расширений Linq, чтобы проверить, равна ли последовательность глав последовательности.

Сравнение двух книг теперь будет простым:

Book book1 = new Book();
Book book2 = new Book();

book1.Title = "A book!";
book2.Title = "A book!";

bool equality = book1.Equals(book2); // returns true

book2.Title = "A different Title";
equality = book1.Equals(book2); // returns false

Имейте в виду, что есть еще один способ реализации равенства: System.IEquatable , которыйиспользуется различными классами в пространстве имен System.Collections для определения равенства.

Я бы сказал, проверьте это, и вы уже в пути!

2 голосов
/ 11 января 2011

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

Для этого я рекомендую два варианта:

A. Используйте универсальный класс сериализации для сериализации обоих объектов и сравнения их значений. Например, у меня есть класс с именем XmlSerializer, который принимает любой объект и сериализует его открытые свойства как документ XML. В этом смысле два объекта, которые имеют одинаковые значения и, возможно, одну и ту же ссылку, будут иметь одинаковые значения.

B. Используя отражение, сравните значения всех свойств обоих объектов, например:

bool Equal(object a, object b)
{
    // They're both null.
    if (a == null && b == null) return true;
    // One is null, so they can't be the same.
    if (a == null || b == null) return false;
    // How can they be the same if they're different types?
    if (a.GetType() != b.GetType()) return false; 
    var Props = a.GetType().GetProperties();
    foreach(var Prop in Props)
    {
        // See notes *
        var aPropValue = Prop.GetValue(a) ?? string.Empty;
        var bPropValue = Prop.GetValue(b) ?? string.Empty;
        if(aPropValue.ToString() != bPropValue.ToString())
            return false;
    }
    return true;
}

Здесь мы предполагаем, что мы можем легко сравнить свойства, например, если они все реализуют IConvertible, или правильно переопределить ToString. Если это не так, я бы проверил, реализуют ли они IConvertible, а если нет, рекурсивно вызовите Equal () для свойств.

Это работает, только если вы довольны сравнением общедоступных свойств. Конечно, вы МОЖЕТЕ также проверить частные и защищенные поля и свойства, но если вы так мало знаете об объектах, вы, вероятно, напрашиваетесь на неприятности, но делаете это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...