Почему нельзя использовать атрибут NonSerialized на уровне класса? Как предотвратить сериализацию класса?

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

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

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

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

-

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

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

2 ответа

Решение

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

Идея состоит в том, что вы создаете объект, который реализует 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. Затем всякий раз, когда средство форматирования встречает делегата, оно использует суррогат вместо прямой сериализации делегата.

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

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

[field: NonSerialized]
public event SomeEventType SomeEventName;

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

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

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

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

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

Другие вопросы по тегам