Могу ли я сериализовать объект типа C #?

Я пытаюсь сериализовать объект Type следующим образом:

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

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

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

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

15.08.2008 14:46:07
Зачем сериализовать тип? Если десериализация не .Net, она не может ее использовать, если это так, то все, что вам нужно передать, это полностью определенное имя.
Keith 15.08.2008 14:48:08
Этот точный код вызывает исключение в .net 6.1: при создании документа XML произошла ошибка. System.RuntimeType недоступен из-за уровня защиты. Только общедоступные типы могут быть обработаны.
YMC 11.01.2017 02:00:08
6 ОТВЕТОВ

Просто посмотрел на его определение, он не помечен как 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)]
1
15.08.2008 14:50:09
Не правда, System.Typeне сериализуемо, но конкретная реализация System.RuntimeTypeесть.
Felix K. 29.05.2012 08:46:26
Теперь он украшен атрибутом Serializable.
bjhuffine 25.08.2015 14:12:26

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

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

2
15.08.2008 14:50:59
РЕШЕНИЕ

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

string typeName = typeof (StringBuilder).FullName;

Затем вы можете сохранить эту строку, как вам необходимо, а затем восстановить тип следующим образом:

Type t = Type.GetType(typeName);

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

object o = Activator.CreateInstance(t);

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

94
15.08.2008 15:12:43
Имейте в виду, что Type.GetType (typeName); будет работать только для типов в той же сборке, что и вызов.
GreyCloud 24.09.2010 16:26:08
решение состоит в том, чтобы использовать AssemblyQualifiedName вместо просто FullName
GreyCloud 24.09.2010 16:31:06
Type.GetType () не будет работать для универсальных типов.
Beriz 22.01.2013 13:25:03

У меня была та же проблема, и я решил создать класс 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

13
13.01.2015 19:30:59
+1 Также, возможно, стоит переопределить toString()возврат return this.Type?.ToString();, чтобы этот класс можно было беспрепятственно использовать везде, где вы используете обычный класс Type.
Zachary Canann 29.08.2017 06:27:16

Ответ Брайана работает хорошо, если тип находится в той же сборке, что и вызов (как указано в одном из комментариев 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;
    }

Я публикую это на тот случай, если кому-то понадобится.

7
23.05.2017 11:46:37

Я сталкивался с этой проблемой, пытаясь выполнить двоичную сериализацию в стандарте .net 2.0. Я решил проблему с помощью пользовательских SurrogateSelectorи SerializationBinder.

Это TypeSerializationBinderбыло необходимо, потому что у фреймворка были проблемы с разрешением, System.RuntimeTypeпрежде чем он получил SurrogateSelector. Я не очень понимаю, почему тип должен быть разрешен до этого шага, хотя ...

Вот код:

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

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

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
25.02.2019 18:44:28