Can I serialize a C# Type object?

前端 未结 6 1528
南方客
南方客 2020-12-04 17:18

I\'m trying to serialize a Type object in the following way:

Type myType = typeof (StringBuilder);
var serializer = new XmlSerializer(typeof(Type));
TextWrit         


        
相关标签:
6条回答
  • 2020-12-04 17:54

    According to the MSDN documentation of System.Type [1] you should be able to serialize the System.Type object. However, as the error is explicitly referring to System.Text.StringBuilder, that is likely the class that is causing the serialization error.

    [1] Type Class (System) - http://msdn.microsoft.com/en-us/library/system.type.aspx

    0 讨论(0)
  • 2020-12-04 17:56

    Just looked at its definition, it is not marked as Serializable. If you really need this data to be serialize, then you may have to convert it to a custom class that is marked as such.

    public abstract class Type : System.Reflection.MemberInfo
        Member of System
    
    Summary:
    Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, and open or closed constructed generic types.
    
    Attributes:
    [System.Runtime.InteropServices.ClassInterfaceAttribute(0),
    System.Runtime.InteropServices.ComDefaultInterfaceAttribute(System.Runtime.InteropServices._Type),
    System.Runtime.InteropServices.ComVisibleAttribute(true)]
    
    0 讨论(0)
  • 2020-12-04 18:00

    Brian's answer works well if the type is in the same assembly as the call (like GreyCloud pointed out in one of the comments). So if the type is in another assembly you need to use the AssemblyQualifiedName as GreyCloud also pointed out.

    However as the AssemblyQualifiedName saves the version, if your assemblies have a different version than the one in the string where you have the type, it won't work.

    In my case this was an issue and I solved it like this:

    string typeName = typeof (MyClass).FullName;
    
    Type type = GetTypeFrom(typeName);
    
    object myInstance = Activator.CreateInstance(type);
    

    GetTypeFrom Method

    private Type GetTypeFrom(string valueType)
        {
            var type = Type.GetType(valueType);
            if (type != null)
                return type;
    
            try
            {
                var assemblies = AppDomain.CurrentDomain.GetAssemblies();                
    
                //To speed things up, we check first in the already loaded assemblies.
                foreach (var assembly in assemblies)
                {
                    type = assembly.GetType(valueType);
                    if (type != null)
                        break;
                }
                if (type != null)
                    return type;
    
                var loadedAssemblies = assemblies.ToList();
    
                foreach (var loadedAssembly in assemblies)
                {
                    foreach (AssemblyName referencedAssemblyName in loadedAssembly.GetReferencedAssemblies())
                    {
                        var found = loadedAssemblies.All(x => x.GetName() != referencedAssemblyName);
    
                        if (!found)
                        {
                            try
                            {
                                var referencedAssembly = Assembly.Load(referencedAssemblyName);
                                type = referencedAssembly.GetType(valueType);
                                if (type != null)
                                    break;
                                loadedAssemblies.Add(referencedAssembly);
                            }
                            catch
                            {
                                //We will ignore this, because the Type might still be in one of the other Assemblies.
                            }
                        }
                    }
                }                
            }
            catch(Exception exception)
            {
                //throw my custom exception    
            }
    
            if (type == null)
            {
                //throw my custom exception.
            }
    
            return type;
        }
    

    I am posting this in case anyone needs it.

    0 讨论(0)
  • 2020-12-04 18:01

    I wasn't aware that a Type object could be created with only a string containing the fully-qualified name. To get the fully qualified name, you can use the following:

    string typeName = typeof (StringBuilder).FullName;
    

    You can then persist this string however needed, then reconstruct the type like this:

    Type t = Type.GetType(typeName);
    

    If you need to create an instance of the type, you can do this:

    object o = Activator.CreateInstance(t);
    

    If you check the value of o.GetType(), it will be StringBuilder, just as you would expect.

    0 讨论(0)
  • 2020-12-04 18:07

    I came across this issue trying to do binary serialization in .net standard 2.0. I ended up solving the problem using a custom SurrogateSelector and SerializationBinder.

    The TypeSerializationBinder was required because the framework was having trouble resolving System.RuntimeType before it got SurrogateSelector. I don't really understand why the type must be resolved before this step though...

    Here is the code:

    // Serializes and deserializes System.Type
    public class TypeSerializationSurrogate : ISerializationSurrogate {
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
            info.AddValue(nameof(Type.FullName), (obj as Type).FullName);
        }
    
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
            return Type.GetType(info.GetString(nameof(Type.FullName)));
        }
    }
    
    // Just a stub, doesn't need an implementation
    public class TypeStub : Type { ... }
    
    // Binds "System.RuntimeType" to our TypeStub
    public class TypeSerializationBinder : SerializationBinder {
        public override Type BindToType(string assemblyName, string typeName) {
            if(typeName == "System.RuntimeType") {
                return typeof(TypeStub);
            }
            return Type.GetType($"{typeName}, {assemblyName}");
        }
    }
    
    // Selected out TypeSerializationSurrogate when [de]serializing Type
    public class TypeSurrogateSelector : ISurrogateSelector {
        public virtual void ChainSelector(ISurrogateSelector selector) => throw new NotSupportedException();
    
        public virtual ISurrogateSelector GetNextSelector() => throw new NotSupportedException();
    
        public virtual ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector) {
            if(typeof(Type).IsAssignableFrom(type)) {
                selector = this;
                return new TypeSerializationSurrogate();
            }
            selector = null;
            return null;
        }
    }
    

    Usage Example:

    byte[] bytes
    var serializeFormatter = new BinaryFormatter() {
        SurrogateSelector = new TypeSurrogateSelector()
    }
    using (var stream = new MemoryStream()) {
        serializeFormatter.Serialize(stream, typeof(string));
        bytes = stream.ToArray();
    }
    
    var deserializeFormatter = new BinaryFormatter() {
        SurrogateSelector = new TypeSurrogateSelector(),
        Binder = new TypeDeserializationBinder()
    }
    using (var stream = new MemoryStream(bytes)) {
        type = (Type)deserializeFormatter .Deserialize(stream);
        Assert.Equal(typeof(string), type);
    }
    
    0 讨论(0)
  • 2020-12-04 18:10

    I had the same problem, and my solution was to create a SerializableType class. It freely converts to and from System.Type, but it serializes as a string. All you have to do is declare the variable as a SerializableType, and from then on you can refer to it as System.Type.

    Here is the class:

    // a version of System.Type that can be serialized
    [DataContract]
    public class SerializableType
    {
        public Type type;
    
        // when serializing, store as a string
        [DataMember]
        string TypeString
        {
            get
            {
                if (type == null)
                    return null;
                return type.FullName;
            }
            set
            {
                if (value == null)
                    type = null;
                else
                {
                    type = Type.GetType(value);
                }
            }
        }
    
        // constructors
        public SerializableType()
        {
            type = null;
        }
        public SerializableType(Type t)
        {
            type = t;
        }
    
        // allow SerializableType to implicitly be converted to and from System.Type
        static public implicit operator Type(SerializableType stype)
        {
            return stype.type;
        }
        static public implicit operator SerializableType(Type t)
        {
            return new SerializableType(t);
        }
    
        // overload the == and != operators
        public static bool operator ==(SerializableType a, SerializableType b)
        {
            // If both are null, or both are same instance, return true.
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }
    
            // If one is null, but not both, return false.
            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }
    
            // Return true if the fields match:
            return a.type == b.type;
        }
        public static bool operator !=(SerializableType a, SerializableType b)
        {
            return !(a == b);
        }
        // we don't need to overload operators between SerializableType and System.Type because we already enabled them to implicitly convert
    
        public override int GetHashCode()
        {
            return type.GetHashCode();
        }
    
        // overload the .Equals method
        public override bool Equals(System.Object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return false;
            }
    
            // If parameter cannot be cast to SerializableType return false.
            SerializableType p = obj as SerializableType;
            if ((System.Object)p == null)
            {
                return false;
            }
    
            // Return true if the fields match:
            return (type == p.type);
        }
        public bool Equals(SerializableType p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return false;
            }
    
            // Return true if the fields match:
            return (type == p.type);
        }
    }
    

    and an example of usage:

    [DataContract]
    public class A
    {
    
        ...
    
        [DataMember]
        private Dictionary<SerializableType, B> _bees;
    
        ...
    
        public B GetB(Type type)
        {
            return _bees[type];
        }
    
        ...
    
    }
    

    You might also consider using AssemblyQualifiedName instead of Type.FullName - see comment by @GreyCloud

    0 讨论(0)
提交回复
热议问题