有没有更好的方法在C#中序列化对象?

时间:2011-04-18 06:39:25

标签: c# generics serialization static deserialization

我的一位同事要求我把这些代码放在这里,看看是否有人能想出更好的方法。

这是从光盘序列化和加载对象的两种方法。加载使用泛型参数来指定正在加载的对象的类型。

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 个答案:

答案 0 :(得分:2)

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并没有真正说出他们做了什么。

serialize方法现在是一种扩展方法。

我添加了一个检查以查看对象类型是否可以序列化。

FormatWith扩展方法可以更轻松地使用字符串格式化:

Console.WriteLine("Hello {0}, {1} is a lovely age!".FormatWith("Jonas", 34));

答案 1 :(得分:1)

根据您要序列化的对象类型,可能会更优化其他序列化程序。例如,ObjectStateFormatter已经过优化,可以序列化和格式化许多常见的.NET Framework引用类型。

答案 2 :(得分:1)

序列化是类的一个方面,通常处理正交 - 这意味着类几乎不应该知道它。 “几乎”因为您遇到的情况是对象不应该被序列化(如代理)或者具有不应序列化的成员。

对于这个发明的序列化属性 - IsSerializable类上甚至有Type属性。这样,除了使用属性之外,对象不必执行任何序列化操作,因此您的方法已经非常好了。坏的是你只在运行时捕获错误而不是编译时间。您可以像ISerializable标记接口一样设置一些安全措施,并为您的IO类添加类型限制,但这可能是不可能的(第三方库)或很多努力(更改所有类)。您甚至可以将序列化卸载到对象,以便他们确切地知道如何进行序列化,但是我怀疑它是值得的,而您应该能够通过单元测试来捕获大多数运行时问题。

我能看到的唯一真正的问题是,您可能会反序列化B类型的对象,因为您实际上正在尝试反序列化类型A。现在我想说你的序列化程序抛出一个无效的强制转换异常或者知道你在完成之前反序列化了错误的类型没有区别 - 在这两种情况下你都遇到了问题并且你的应用程序无法完成它应该做的事情。 / p>

您可以使用a custom serialization binder解决BinaryFormatter的版本问题,或者您可能需要考虑使用xml序列化程序。

答案 3 :(得分:0)

BinaryFormatter还序列化了定义类型的程序集版本,并在加载了另一个版本进行反序列化时失败(如果我没记错的话)。这意味着如果仅序列化数据,您应该能够使用更少的空间存储它。

您可以在序列化对象时检查结果,并考虑“更好”的方式来存储信息,但大多数替代方案也会略微不同(功能上)。