2008-08-15 4 views
40

Ich versuche, ein Type-Objekt in der folgenden Art und Weise zu serialisiert:Kann ich ein C# -Typ-Objekt serialisieren?

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

Wenn ich das tue, serialisiert der Anruf wirft die folgende Ausnahme:

„Der Typ System.Text. StringBuilder wurde nicht erwartet Verwenden Sie das XmlInclude- oder SoapInclude-Attribut , um Typen anzugeben, die nicht statisch bekannt sind . "

Gibt es eine Möglichkeit für mich, das Type Objekt zu serialisieren? Beachten Sie, dass ich nicht versuche, die StringBuilder selbst zu serialisieren, sondern das Objekt Type, das die Metadaten zur Klasse StringBuilder enthält.

+1

Warum den Typ serialisieren? Wenn die Deserialisierung nicht .Net ist, kann sie sie nicht verwenden, wenn Sie dann nur den vollständig qualifizierten Namen übergeben müssen. – Keith

+0

Dieser genaue Code löst Ausnahme in .net 6.1: Es gab einen Fehler beim Generieren des XML-Dokuments. Auf System.RuntimeType kann aufgrund seiner Schutzstufe nicht zugegriffen werden. Nur öffentliche Typen können verarbeitet werden. – YMC

Antwort

73

ich nicht bewusst war, dass ein Typ-Objekt mit nur einer Zeichenfolge erstellt werden, um den vollständig qualifizierten Namen enthält. Um den vollständig qualifizierten Namen zu bekommen, können Sie die folgenden Schritte aus:

string typeName = typeof (StringBuilder).FullName; 

Anschließend können Sie diese Zeichenfolge jedoch erforderlich andauern, rekonstruieren dann die Art wie folgt aus:

Type t = Type.GetType(typeName); 

Wenn Sie eine Instanz erstellen von der Art, können Sie dies tun:

object o = Activator.CreateInstance(t); 

Wenn Sie den Wert von o.GetType() überprüfen, wird es String sein würde wie Sie es erwarten.

+4

Seien Sie gewarnt, dass Type.GetType (typeName); funktioniert nur für Typen in derselben Baugruppe wie der Aufruf. – GreyCloud

+33

Die Lösung besteht darin, AssemblyQualifiedName statt nur FullName – GreyCloud

+5

Type.GetType() wird für generische Typen fehlschlagen. – Beriz

1

Nur seine Definition betrachtet, ist es nicht als Serializable markiert. Wenn Sie diese Daten wirklich serialisieren müssen, müssen Sie sie möglicherweise in eine benutzerdefinierte Klasse konvertieren, die als solche gekennzeichnet ist.

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

Nicht wahr, 'System.Type' ist nicht serialisierbar, aber die konkrete Implementierung' System.RuntimeType' ist. –

+0

Jetzt ist es mit dem Serializable-Attribut verziert. – bjhuffine

2

Nach der MSDN-Dokumentation von System.Type [1] sollten Sie das System.Type Objekt serialisiert werden können. Da sich der Fehler jedoch explizit auf System.Text.StringBuilder bezieht, ist dies wahrscheinlich die Klasse, die den Serialisierungsfehler verursacht.

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

10

Ich hatte das gleiche Problem, und meine Lösung war eine SerializableType-Klasse zu erstellen. Es konvertiert frei zu und von System.Type, aber es serialisiert als eine Zeichenfolge. Alles, was Sie tun müssen, ist die Variable als SerializableType zu deklarieren, und von da an können Sie sie als System.Type bezeichnen. Hier

ist die Klasse:

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

und ein Beispiel für die Nutzung:

[DataContract] 
public class A 
{ 

    ... 

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

    ... 

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

    ... 

} 

Sie auch interessieren könnten AssemblyQualifiedName statt Type.FullName - siehe Kommentar von @GreyCloud

+1

+1 sollte die Antwort laut der OP-Frageformulierung sein – Askolein

+1

+1 Außerdem kann es sich lohnen, 'toString()' ebenfalls zu überschreiben, um 'return this.Type? .ToString();' zurückzugeben, so dass diese Klasse sein kann nahtlos überall dort verwendet werden, wo Sie eine normale Type-Klasse verwenden würden. –

5

Brian's Antwort funktioniert gut, wenn der Typ in der gleichen Baugruppe wie der Aufruf ist (wie GreyCloud in einem der Kommentare hingewiesen). Also, wenn der Typ in einer anderen Baugruppe ist, müssen Sie die AssemblyQualifiedName verwenden, wie GreyCloud auch darauf hingewiesen.

Wie jedoch die AssemblyQualifiedName die Version speichert, wenn Ihre Assemblies eine andere Version als die in der Zeichenfolge haben, wo Sie den Typ haben, wird es nicht funktionieren.

In meinem Fall war dies ein Problem, und ich löste es wie folgt aus:

string typeName = typeof (MyClass).FullName; 

Type type = GetTypeFrom(typeName); 

object myInstance = Activator.CreateInstance(type); 

GetTypeFrom Methode

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

ich dies im Falle bin Entsendung jemand es braucht.

Verwandte Themen