When implementing the ISerializable
interface in C#, we provide a constructor which takes a SerializationInfo
object, and then queries it with various GetInt32
, GetObject
etc. methods in order to fill the fields of the object which we are trying to deserialize.
One major reason to implement this interface, rather than just using the [Serializable]
attribute, is for backwards compatibility: if we have added new fields to the class at some point, we can catch the SerializationException
thrown by a serialized, older version of the class, and handle them in an appropriate manner.
My question is the following: why do we have to use these exceptions for what is, essentially, control flow? If I am deserializing a large number of classes which were saved some time ago, potentially each missing field in each class will throw an exception, causing really bad performance.
Why does the SerializationInfo
class not provide TryGetValue
methods which would simply return false
if the name string were not present?
You can iterate over the available fields and use switch
, though...
foreach(SerializationEntry entry in info) {
switch(entry.Name) {
...
}
}
Or you could use protobuf-net ;-p
Well no one answered 'why', but I'm guessing that's addressed to MS..
My implementation for anyone in need:
public static class SerializationInfoExtensions
{
public static bool TryGetValue<T>(this SerializationInfo serializationInfo, string name, out T value)
{
try
{
value = (T) serializationInfo.GetValue(name, typeof(T));
return true;
}
catch (SerializationException)
{
value = default(T);
return false;
}
}
public static T GetValueOrDefault<T>(this SerializationInfo serializationInfo, string name, Lazy<T> defaultValue)
{
try
{
return (T) serializationInfo.GetValue(name, typeof(T));
}
catch (SerializationException)
{
return defaultValue.Value;
}
}
}
In addition to the enumerating solution, there is an internal method SerializationInfo.GetValueNoThrow()
according to the Reference Source.
You can e.g. make an extension method like this and avoid the exception overhead:
static class SerializationInfoExtensions
{
private static MethodInfo _GetValueNoThrow =
typeof(SerializationInfo).GetMethod("GetValueNoThrow",
BindingFlags.Instance | BindingFlags.NonPublic);
public static Object GetValueNoThrow(this SerializationInfo info, String name, Type type)
{
return _GetValueNoThrow.Invoke(info, new object[] { name, type });
}
}
This solution still results in some reflection overhead compared to a plain method call, but it is an order of magnitude smaller – a crude benchmark at .NET Fiddle:
Method | Mean | Error | StdDev |
---|---|---|---|
ExceptionOverhead | 72.840 ms | 2.0690 ms | 2.2997 ms |
ReflectionOverhead | 2.657 ms | 0.0192 ms | 0.0205 ms |
However, it should be noted that the whole SerializationInfo
type is going to be deprecated in a future version of .NET, as discussed in Add SerializationInfo.TryGetValue API #42460.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With