Есть ли лучший способ сериализации объектов в C #? - PullRequest
0 голосов
/ 18 апреля 2011

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

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

public static class IO
{
    public static void SaveObject(string path, object obj)
    {
        using (Stream stream = File.Open(path, FileMode.Create))
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, obj);
        }
    }

    public static T LoadObject<T>(string path)
    {
        using (Stream stream = File.Open(path, FileMode.Open))
        {
            IFormatter formatter = new BinaryFormatter();
            T obj = (T)formatter.Deserialize(stream);
            return obj;
        }
    }
}

Ответы [ 4 ]

2 голосов
/ 18 апреля 2011
public static class Serialization
{
    private static void ValidateSerializable(this Type type)
    {
        bool isSerializable = !typeof(ISerializable).IsAssignableFrom(type);
        bool hasSerilizationAttribute = type.GetCustomAttributes(typeof(SerializableAttribute), false).Length == 1;
        if (!isSerializable && !hasSerilizationAttribute)
            throw new SerializationException("'{0}' is not marked as serializable!".FormatWith(type.FullName));
    }

    public static void SerializeToBinaryFile(this object instance, string path)
    {
        instance.GetType().ValidateSerializable();

        using (Stream stream = File.Open(path, FileMode.Create))
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, instance);
        }
    }

    public static T DeserializeFromBinaryFile<T>(string path) where T : class
    {
        typeof(T).ValidateSerializable();

        using (Stream stream = File.Open(path, FileMode.Open))
        {
            IFormatter formatter = new BinaryFormatter();
            return (T)formatter.Deserialize(stream);
        }
    }

    public static string FormatWith(this string instance, params  object[] arguments)
    {
        return string.Format(instance, arguments);
    }
}

[Serializable]
public class User
{
    public string FirstName { get; set; }
}

class Program
{
    protected void Main(string[] argv)
    {
        User user = new User {FirstName = "Jonas"};
        user.SerializeToBinaryFile("myUser.bin");

        User deserializedUser = Serialization.DeserializeFromBinaryFile<User>("myUser.bin");
    }
}

Я изменил имена методов, чтобы отразить, к чему они. Load на самом деле не говорит, что они делают.

Метод сериализации теперь является методом расширения.

Я добавил проверку, чтобы проверить, можно ли сериализовать тип объекта.

Метод расширения FormatWith упрощает работу с форматированием строки:

Console.WriteLine("Hello {0}, {1} is a lovely age!".FormatWith("Jonas", 34));
1 голос
/ 18 апреля 2011

Хорошо, сериализация - это аспект к классам, который обычно рассматривается как ортогональный, то есть классы должны почти ничего о нем не знать. «Почти», потому что у вас есть случаи, когда объекты не должны быть сериализованы (например, прокси) или есть члены, которые не должны быть сериализованы.

Для этого были созданы атрибуты сериализации - даже в классе Type есть свойство IsSerializable. Таким образом, ваш подход уже очень хорош в том смысле, что объектам не нужно ничего делать для сериализации, кроме использования атрибутов. Плохо то, что вы ловите ошибки только во время выполнения, а не во время компиляции. Вы можете установить некоторые меры безопасности, такие как интерфейс маркера ISerializable, и добавить ограничение типа в свой класс IO, но это может быть невозможно (сторонние библиотеки) или много усилий (изменение всех классов вокруг). Вы могли бы даже пойти так далеко, чтобы переложить сериализацию на объекты, чтобы они точно знали, как де-сериализовать, но я сомневаюсь, что это стоит усилий, тогда как вы сможете уловить большинство проблем во время выполнения с юнит-тестами.

Единственная реальная проблема, которую я вижу, состоит в том, что вы можете десериализовать объект типа B, когда вы фактически пытаетесь десериализовать тип A. Теперь я бы сказал, что нет никакой разницы в том, что ваш сериализатор генерирует недопустимое исключение приведения или знает, что вы десериализовали неправильный тип, прежде чем закончите - в обоих случаях у вас есть проблема, и ваше приложение не может делать то, что должно.

Вы можете обойти проблемы с версией BinaryFormatter с помощью пользовательского связывателя сериализации или вы можете рассмотреть возможность использования сериализатора xml.

1 голос
/ 18 апреля 2011

В зависимости от типа объекта, который вы хотите сериализовать, другой сериализатор может быть более оптимизирован. Например, ObjectStateFormatter оптимизирован для сериализации и форматирования многих распространенных ссылочных типов .NET Framework.

0 голосов
/ 18 апреля 2011

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

Вы можете проверить результат при сериализации объекта и подумать о «лучших» способах хранения информации, но большинство альтернатив также делают вещи немного иначе (функционально).

...