Format of generic type namein SerializationBinder

2019-09-19 02:34发布

问题:

I'm using a SerializationBinder to bind to the current types in case of changes of type names. Therefore I have a dictionary that binds old type names to the new names.

dict.Add("Old.Namespace.OldTypeName", "New.Namespace.NewName");

I can use this dictionary to get the type in the Type BindToType(string assemblyName, string typeName) method of the SerializationBinder. If this type is used in generic types, I must also find the new type of the generic. This answer explained how to do this for List<T>. I am wondering if there is a standardized format of generic type names, so that I can use some kind of regex to get the new type for all kind of generics.

public class TypeDictionary
{
    private Dictionary<string, Type> bindings_ =
        new Dictionary<string, Type>();

    public void AddBinding(string oldTypeString, Type newType)
    {
        bindings_.Add(oldTypeString, newType);
    }

    public Type NewType(string oldTypeString)
    {
        // How should this be implemented:
        if (IsGeneric(oldTypeString))
            return NewGenericType(oldTypeString);

        Type newType;
        if (bindings_.TryGetValue(oldTypeString, out newType))
            return newType;
        return null;
    }
 }

Most strings I have seen so far are as List<T> like this

 Namespace.TypeName`1[[MyAssembly.MyClass, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]

Is it save to just look for "[[MyAssembly.MyClass," to detect a generic Type?

回答1:

The full name of the generic type contains a backtick character (`) followed by a digit that specifies the number of type arguments, followed by a square bracket containing each type argument (possibly in in an additional square bracket).

So List<String> is written like

  List`1[[String]]

and Dictionary<String, Object> like

  Dictionary`2[[String],[Object]]

and Func<String, Object, Guid> like

  Func`3[[String],[Object],[Guid]]

except that full namespace is given for the generic type, and full namespace, assembly, version, culture, and public key token are given for each type argument.

On the other hand, the full name of a non-generic type contains neither backtricks nor square brackets. Maybe you can write your regex with this in mind?

PS! If you use oldType.ToString() instead of oldType.FullName you get a somewhat shorter string that does not include assembly, culture, etc.



回答2:

Presumably, you want to replace all references to MyClass, whether they are generic type parameters of other classes or not.

So, you just need to look for your type names anywhere in the type name string.

If you replace all the type names in your dictionary, you get a new string which you can pass to Type.GetType( string typeName ). This gives you the new Type instance which you can return from BindToType.

It gets a bit tricky when the types you are replacing are themselves generic.



回答3:

You do not need any complex procedure. Suppose you are about to deserialize a type name like this:

System.Collections.Generic.List`1[[Myclass, MyNameSpace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]

Simply, handle AppDomain.AssemblyResolve event and then call Type.GetType method with above type name. It will raise the event and there, you can return whatever assembly you want.