Ошибка циклической ссылки при сериализации объектов с помощью пользовательских методов Equals и GetHashCode - PullRequest
1 голос
/ 23 декабря 2011

Я получаю Обнаружена циклическая ссылка при сериализации объекта типа T Ошибка при возврате моего объекта в результате WebMethod моего веб-сервиса asmx.

Если я удалю Equals и GetHashCodeиз класса Т проблема исчезает.У меня нет циклических ссылок, поэтому похоже, что сериализация обнаруживает циклические ссылки путем сравнения объектов, и, если они равны, она думает, что есть круги.класс для сериализации, как это делают многие, и затем копирование данных из одного в другой, но я хочу иметь возможность сделать это в одном классе, чтобы избежать параллельных иерархий классов как одного из запахов кода .

Я хочу иметь возможность определять Equals, GetHashCode и сохранять его Сериализуемый .Как?

Ответы [ 3 ]

1 голос
/ 16 марта 2015

Позвольте мне перейти к корню проблемы. Потому что у меня была та же проблема.

Я понял, что проблема не в методе GetHashCode, а в методе Equals.

Самая важная причина, по которой XmlSerializer создает исключение такого рода, связана со структурой XML-документа и «механизмом равенства» внутри XmlSerializer.

Например:

private XmlSerializer serializer;

public void TryToSerialize(TextWriter output)
{
    MyObject instance = new MyObject();
    instance.Key = 101;
    instance.SomeValue = "Some value";
    instance.Child = new MyObject();
    instance.Child.Key = 101;
    instance.Child.SomeValue = "Another value";

    serializer.Serialize(output, instance);
}

А как я реализовал GetHashCode-метод и метод Equals?

Как это:

public overrides int GetHashCode()
{
    return this.Key.GetHashCode();
}

public overrides bool Equals(object obj)
{
    if(obj == null) return false;

    MyObject other = obj as MyObject;
    if(other == null) return false;

    return this.Key.Equals(other.Key);
}

А что будет, если я запусту метод "TryToSerialize"? Я получу InvalidOperationException с сообщением При сериализации объекта типа T .

была обнаружена циклическая ссылка.

При сериализации XmlSerializer пытается избежать добавления того же объекта, что и дочерний, в XML-документ, потому что это приведет к кружку. Но способ проверить, «это тот же самый объект», состоит в том, чтобы использовать метод GetHashCode и метод Equals для того, для чего они предназначены - чтобы проверить равенство объектов.

В нашем примере эти объекты могут быть разными экземплярами, и XmlSerializer не проверяет «экземпляры в памяти», а использует известные ему методы - GetHashCode и Equals.

Итак, подумайте, как реализовать ваш метод Equals

Или лучше ...

Подумайте, как вы можете улучшить реализацию ваших классов и методов, чтобы избежать этой проблемы в корне проблемы . ; -)

0 голосов
/ 24 августа 2018

В моем случае я решил реализовать (автоматическое завершение кода Visual Studio) правильно методы Equals и GetHashCode, например:

 public override bool Equals(object obj)
        {
            var permission = obj as Permission;
            return permission != null &&
                   EqualityComparer<AccessLevel>.Default.Equals(Access, permission.Access) &&
                   EqualityComparer<Functionality>.Default.Equals(Function, permission.Function);
        }

        public override int GetHashCode()
        {
            var hashCode = -720887508;
            hashCode = hashCode * -1521134295 + EqualityComparer<AccessLevel>.Default.GetHashCode(Access);
            hashCode = hashCode * -1521134295 + EqualityComparer<Functionality>.Default.GetHashCode(Function);
            return hashCode;
        }

предыдущий код был только методом Equals и так:

return (Access.Name.Equals((Permission)obj.Name)...
0 голосов
/ 23 декабря 2011

Один из способов сделать это - унаследовать каждый класс, участвующий в сериализации, от одного интерфейса, который определяет метод OnSerializing и реализует этот метод в каждом классе, чтобы вызывать его для подходящих потомков. Также имейте сериализованный приватный член bool в каждом классе со значением false по умолчанию и установите для него значение true в OnSerializing. Это позволит вызывать его на корне и распространять на все сериализуемые узлы до каждого листа.

Вызовите returnValue.OnSerializing () прямо перед возвратом сериализуемого returnValue из WebMethod.

Каждый раз, когда вы переопределяете значение Equals, первая строка должна быть if(Serializing)return base.Equals(obj);, первая строка в GetHashCode должна быть if(Serializing)return base.GetHashCode();

Таким образом, перед возвратом из WebMethod с помощью OnSerializing вы отмечаете целое дерево, чтобы отключить настройку Equals и GetHashCode.

Если вам нужно что-то сделать после сериализации (например, сохранить на диске и затем вернуться), тогда определите OnSerialized и распространяйте по дереву таким же образом, чтобы установить для флага сериализации значение false.

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

...