Is there a better way of serializing objects in C#?
One of my colleagues challenged me to put this code up here to see if anyone can think of a better way of doing this.开发者_如何学Go
It's two methods to serialize and load objects from disc. The load uses a generic parameter to specify the type of object that is being loaded.
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;
}
}
}
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");
}
}
I've change the method names to reflect what they to. Load
doesnt really say what they do.
The serialize method is now an extension method.
I've added a check to see if the object type can be serialized.
The FormatWith extension method makes it easier to work with string formatting:
Console.WriteLine("Hello {0}, {1} is a lovely age!".FormatWith("Jonas", 34));
Well serialization is an aspect to the classes which is usually treated orthogonal - meaning the classes should know almost nothing about it. "Almost" because you have cases where objects should not be serialized (like proxies) or have members which should not be serialized.
For this the serialization attributes where invented - there is even an IsSerializable
property on the Type
class. In that way your approach is already very good in the sense that objects don't have to do anything to be serialized except using the attributes. The bad thing is that you only catch errors at runtime and not compile time. You could put some safeguards in place like an ISerializable
marker interface and add a type restriction to your IO
class but that might not be possible (3rd party libs) or a lot of effort (changing all classes around). You could even go that far to offload the serialization to the objects so they know exactly how to de/serialize but I doubt it's worth the effort whereas you should be able to catch most runtime problems with unit tests.
The only real problem I can see is that you might deserialize an object of type B
where as you are actually trying to deserialize type A
. Now I would say there is no difference to your serializer throwing an invalid cast exception or knowing that you are deserializing the wrong type before you have finished - in both cases you have a problem and your application can't do what it's supposed to.
You can get around version problems with the BinaryFormatter
with a custom serialization binder or you might want to consider a xml serializer.
Depending on what kind of object you want to serialize, a different serializer might be more optimized. For example the ObjectStateFormatter
is optimized to serialize and format many common .NET Framework reference types.
The BinaryFormatter
also serializes the assembly version the type was defined in, and fails (if I remember correctly) when deserialized with another version loaded. This means that if serializing the data only, you should be able to store it using less space.
You could inspect the result when serializing an object, and think of "better" ways to store information, but most alternatives also do things slightly different (functionally).
精彩评论