2008-08-15 6 views
40

Я пытаюсь сериализовать объект типа следующим образом:Могу ли я сериализовать объект типа C#?

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

Когда я делаю это, вызов Сериализация бросает следующее исключение:

«Тип System.Text. StringBuilder не ожидал. Используйте атрибут X12Invlate или SoapInclude , чтобы указать типы, которые не являются известными статически. "

Есть ли способ для сериализации объекта Type? Обратите внимание, что я не пытаюсь сериализовать сам StringBuilder, но объект Type, содержащий метаданные о классе StringBuilder.

+1

Зачем сериализовать тип? Если десериализация не является .Net, она не может ее использовать, если это то, что вам нужно передать, это полное имя. – Keith 2008-08-15 14:48:08

+0

Этот точный код вызывает исключение в .net 6.1: произошла ошибка, генерирующая XML-документ. System.RuntimeType недоступен из-за уровня защиты. Могут обрабатываться только общедоступные типы. – YMC 2017-01-11 02:00:08

ответ

73

Я не знал, что объект типа может быть создан только на строку, содержащую полное имя. Для того, чтобы получить полное имя, вы можете использовать следующее:

string typeName = typeof (StringBuilder).FullName; 

Вы можете упорствовать эту строку, однако необходимо, а затем реконструировать тип вроде этого:

Type t = Type.GetType(typeName); 

Если вам необходимо создать экземпляр типа, вы можете сделать это:

object o = Activator.CreateInstance(t); 

Если вы проверяете значение o.GetType(), она будет StringBuilder, так же, как и следовало ожидать.

1

Просто посмотрел на его определение, оно не помечено как Serializable. Если вам действительно нужны эти данные для сериализации, вам может потребоваться преобразовать их в пользовательский класс, который помечен как таковой.

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

Неверно, `System.Type` не сериализуем, а конкретная реализация` System.RuntimeType`. – 2012-05-29 08:46:26

+0

Теперь он украшен атрибутом Serializable. – bjhuffine 2015-08-25 14:12:26

2

В соответствии с документацией MSDN System.Type [1] вы можете сериализовать объект System.Type. Однако, поскольку ошибка явно ссылается на System.Text.StringBuilder, это, скорее всего, класс, вызывающий ошибку сериализации.

[1] Тип класса (Система) - http://msdn.microsoft.com/en-us/library/system.type.aspx

10

У меня была такая же проблема, и моим решением было создать класс SerializableType. Он свободно конвертируется в System.Type и из него, но сериализуется как строка. Все, что вам нужно сделать, это объявить переменную как SerializableType, и с этого момента вы можете ссылаться на нее как на System.Type.

Вот класс:

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

и пример использования:

[DataContract] 
public class A 
{ 

    ... 

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

    ... 

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

    ... 

} 

Вы могли бы также рассмотреть возможность использования AssemblyQualifiedName вместо Type.FullName - см комментарий по @GreyCloud

5

Brian's ответ хорошо работает, если тип находится в той же самой сборке, что и вызов (например, GreyCloud указал в одном из комментариев). Итак, если тип находится в другой сборке, вам нужно использовать AssemblyQualifiedName, как указал GreyCloud.

Однако, поскольку AssemblyQualifiedName сохраняет версию, если ваши сборки имеют другую версию, отличную от той, в которой у вас есть тип, это не сработает.

В моем случае это был вопрос, и я решил ее так:

string typeName = typeof (MyClass).FullName; 

Type type = GetTypeFrom(typeName); 

object myInstance = Activator.CreateInstance(type); 

GetTypeFrom Метод

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

Я отправляю это в случае, если кто нуждается в этом.