BinaryFormatter Справочник по функциям
Впервые BinaryFormatter был представлен первоначальный выпуск платформа .NET Framework в 2002 году. Чтобы понять, как заменить использование BinaryFormatter, он помогает узнать, как BinaryFormatter работает.
BinaryFormatter может сериализовать любой экземпляр любого типа, помеченного или [Serializable]
реализующего ISerializable интерфейс.
Имена членов
В большинстве случаев тип аннотируется и [Serializable]
сериализатор использует отражение для сериализации всех полей (как общедоступных, так и не открытых), за исключением тех, с которыми задается аннотация [NonSerialized]
. По умолчанию сериализованные имена элементов будут соответствовать именам полей типа. Это исторически привело к несовместимости, если даже частные поля переименованы в [Serializable]
типы. Во время миграции от BinaryFormatterнего необходимо понять, как сериализованные имена полей обрабатываются и переопределяются.
Автоматические свойства C#
Для C# автоматически реализованные свойства ({ get; set; }
) BinaryFormatter сериализуют резервные поля, созданные компилятором C#, а не свойства. Имена этих сериализованных полей резервной копии содержат недопустимые символы C# и не могут контролироваться. Декомпилатор C# (например https://sharplab.io/ , ILSpy) может продемонстрировать, как автоматические свойства C# отображаются в среде выполнения.
[Serializable]
internal class PropertySample
{
public string Name { get; set; }
}
Предыдущий класс преобразуется компилятором C# в:
[Serializable]
internal class PropertySample
{
private string <Name>k__BackingField;
public string Name
{
get
{
return <Name>k__BackingField;
}
set
{
<Name>k__BackingField = value;
}
}
}
В этом случае <Name>k__BackingField
— это имя элемента, который BinaryFormatter
используется в сериализованной полезных данных. Невозможно использовать nameof
или любой другой оператор C#, чтобы получить это имя.
Интерфейс ISerializable поставляется с GetObjectData методом, который позволяет пользователям управлять именами с помощью одного из AddValue методов.
// Note lack of any special attribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", this.Name);
}
Если такая настройка была применена, необходимо также предоставить информацию во время десериализации. Это возможно с помощью конструктораserialization, где все значения считываются с SerializationInfo помощью одного из Get
методов, которые он предоставляет.
private PropertySample(SerializationInfo info, StreamingContext context)
{
this.Name = info.GetString("Name");
}
Примечание.
Оператор nameof
не использовался здесь, так как полезные данные могут быть сохранены, и свойство может быть переименовано позже. Таким образом, даже если он получает переименование (скажем, FirstName
потому что вы решили также ввести LastName
свойство), чтобы оставаться обратно совместимым, serialization следует по-прежнему использовать старое имя, которое может быть сохранено где-то.
Serialization сноповязалка
Рекомендуется использовать SerializationBinder для управления загрузкой классов и мандатом на загрузку класса. Это сводит к минимуму уязвимости безопасности (поэтому только разрешенные типы загружаются, даже если злоумышленник изменяет полезные данные для десериализации и загрузки чего-либо другого).
Для использования этого типа требуется наследование от него и переопределение BindToType метода.
В идеале список сериализуемых типов закрыт, так как это означает, что вы знаете, какие типы можно создать, что поможет снизить уязвимости системы безопасности.