2008-08-15 19 views
40

estoy tratando de serializar un objeto Type de la siguiente manera:¿Puedo serializar un objeto tipo C#?

Type myType = typeof (StringBuilder); 
var serializer = new XmlSerializer(typeof(Type)); 
TextWriter writer = new StringWriter(); 
serializer.Serialize(writer, myType); 

Cuando hago esto, la llamada a tiros Serialice la siguiente excepción:

"El tipo System.Text. No se esperaba StringBuilder. Use el atributo XmlInclude o SoapInclude para especificar los tipos que no son conocidos estáticamente. "

¿Hay una manera para mí para serializar el objeto Type? Tenga en cuenta que no estoy tratando de serializar el StringBuilder sí mismo, pero el objeto Type que contiene los metadatos sobre la clase StringBuilder.

+1

¿Por qué serializar el tipo? Si la deserialización no es .Net no puede usarlo, si es así, todo lo que necesita pasar es el nombre completo. – Keith

+0

Este código exacto genera una excepción en .net 6.1: Hubo un error al generar el documento XML. System.RuntimeType es inaccesible debido a su nivel de protección. Solo los tipos públicos pueden ser procesados. – YMC

Respuesta

73

yo no era consciente de que un objeto tipo se podría crear con sólo una cadena que contiene el nombre completo. Para obtener el nombre completo, puede utilizar el siguiente:

string typeName = typeof (StringBuilder).FullName; 

Luego, puede persistir esta cadena sin embargo necesario, entonces reconstruir el tipo de la siguiente manera:

Type t = Type.GetType(typeName); 

Si es necesario crear una instancia del tipo, se puede hacer esto:

object o = Activator.CreateInstance(t); 

Si comprueba el valor de o.GetType(), será StringBuilder, tal como era de esperar.

+4

Se advirtió que Type.GetType (typeName); solo funcionará para los tipos en el mismo ensamblaje que la llamada. – GreyCloud

+33

la solución es usar AssemblyQualifiedName en lugar de simplemente FullName – GreyCloud

+5

Type.GetType() fallará para los tipos genéricos. – Beriz

1

Acabo de ver su definición, no está marcado como Serializable. Si realmente necesita que se serialicen estos datos, es posible que deba convertirlos a una clase personalizada que esté marcada como tal.

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)] 
+2

No es cierto, 'System.Type' no es serializable pero sí lo es la implementación concreta' System.RuntimeType'. –

+0

Ahora está decorado con el atributo Serializable. – bjhuffine

2

De acuerdo con la documentación de MSDN de System.Type [1], debería poder serializar el objeto System.Type. Sin embargo, como el error se refiere explícitamente a System.Text.StringBuilder, es probable que la clase que está causando el error de serialización.

[1] Tipo Clase (Sistema) - http://msdn.microsoft.com/en-us/library/system.type.aspx

10

Tuve el mismo problema, y ​​mi solución fue crear una clase SerializableType. Se convierte libremente hacia y desde System.Type, pero se serializa como una cadena. Todo lo que tiene que hacer es declarar la variable como un tipo serializable, y desde ese momento puede referirse a él como tipo de sistema.

Aquí es la clase:

// 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); 
    } 
} 

y un ejemplo de uso:

[DataContract] 
public class A 
{ 

    ... 

    [DataMember] 
    private Dictionary<SerializableType, B> _bees; 

    ... 

    public B GetB(Type type) 
    { 
     return _bees[type]; 
    } 

    ... 

} 

También puede considerar el uso de AssemblyQualifiedName en lugar de Type.FullName - véase el comentario de @GreyCloud

+1

+1 debería ser la respuesta según la redacción de la pregunta de OP – Askolein

+1

+1 Además, puede valer anular 'toString()' y devolver 'return this.Type? .ToString();', de modo que esta clase pueda ser utilizado sin interrupciones en cualquier lugar en el que use una clase Type normal. –

5

Brian's la respuesta funciona bien si el tipo está en el mismo ensamblaje que la llamada (como señaló GreyCloud en uno de los comentarios). Por lo tanto, si el tipo está en otro ensamblaje, debe usar el AssemblyQualifiedName como también señaló GreyCloud.

Sin embargo, como AssemblyQualifiedName guarda la versión, si sus ensamblajes tienen una versión diferente a la de la cadena donde tiene el tipo, no funcionará.

En mi caso esto fue un problema y lo resolvió así:

string typeName = typeof (MyClass).FullName; 

Type type = GetTypeFrom(typeName); 

object myInstance = Activator.CreateInstance(type); 

GetTypeFrom Método

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; 
    } 

Estoy publicar esto en caso de que alguien lo necesita.

Cuestiones relacionadas