Почему нельзя использовать атрибут NonSerialized на уровне класса? Как предотвратить сериализацию класса? - PullRequest
3 голосов
/ 18 марта 2010

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

Допустим, я прикрепил обработчик к PriceChanged. Когда код пытается сериализовать PriceChanged, он выдает исключение, что обработчик не помечается как сериализуемый.

Мои альтернативы:

  • Я не могу легко удалить все обработчики из события перед сериализацией
  • Я не хочу помечать обработчик как сериализуемый, потому что мне нужно было бы также рекурсивно пометить все зависимости обработчиков.
  • Я не хочу помечать PriceChanged как NonSerialized - существуют десятки подобных событий, которые потенциально могут иметь обработчики. РЕДАКТИРОВАТЬ: Еще одна причина, почему я не могу сделать это, потому что классы данных (и, следовательно, события) генерируются, и я не имею прямого контроля над кодом генерации. В идеале код генерации должен просто пометить все события как несериализированные.
  • В идеале, я бы хотел, чтобы .NET просто прекратил спускаться по графу объектов в этой точке и сделал это «листом». Так почему же .NET не позволяет пометить весь класс как несериализуемый?

-

Наконец-то я обошел эту проблему, заставив обработчик реализовать ISerializable и ничего не делая в конструкторе сериализации / GetDataObject. Но обработчик по-прежнему сериализован, просто со всеми его зависимостями, установленными в нуль - так что я должен был это учитывать.

Есть ли лучший способ предотвратить сериализацию всего класса? То есть тот, который не требует учета нулевых зависимостей?

Ответы [ 2 ]

4 голосов
/ 18 марта 2010

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

Идея состоит в том, что вы создаете объект, который реализует ISerializationSurrogate и в основном делает то, что вы уже делаете - ничего в методах GetObjectData и SetObjectData. Разница в том, что вы будете настраивать сериализацию делегата, а не класс, содержащий его.

Что-то вроде:

class DelegateSerializationSurrogate : ISerializationSurrogate {
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        // do nothing
    }
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context) {
        // do nothing
        return null;
    }
}

Затем вы регистрируете это в программе форматирования, используя процедуры , описанные в этом столбце MSDN . Затем всякий раз, когда средство форматирования встречает делегата, оно использует суррогат вместо прямой сериализации делегата.

2 голосов
/ 18 марта 2010

... есть десятки событий ...

Лично я бы просто добавил несериализованные маркеры, которые для полевых событий проще всего сделать с помощью:

[field: NonSerialized]
public event SomeEventType SomeEventName;

(вам не нужно добавлять делегата поддержки вручную)

Каковы ваши требования к сериализации? BinaryFormatter во многих отношениях наименее дружелюбный из сериализаторов; последствия для событий немного уродливы, и при хранении они очень хрупкие (IMO действительно подходит только для транспорта, а не для хранения).

Тем не менее, Есть много хороших альтернатив, которые поддерживали бы наиболее распространенные сценарии «глубокого клонирования»:

  • XmlSerializer (но только для публичных участников)
  • DataContractSerializer / NetDataContractSerializer
  • protobuf-net (который включает Serializer.DeepClone для этой цели)

(обратите внимание, что в большинстве из этих сериализаций поддержка требует дополнительных атрибутов, поэтому не сильно отличается от добавления атрибутов [NonSerialized] во-первых!)

...