我可以序列化 C# 类型对象吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-01 08:44:16  来源:igfitidea点击:

Can I serialize a C# Type object?

提问by Brian Sullivan

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

我正在尝试通过以下方式序列化 Type 对象:

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

When I do this, the call to Serialize throws the following exception:

当我这样做时,对 Serialize 的调用会引发以下异常:

"The type System.Text.StringBuilder was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

“不需要 System.Text.StringBuilder 类型。使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型。”

Is there a way for me to serialize the Typeobject? Note that I am not trying to serialize the StringBuilderitself, but the Typeobject containing the metadata about the StringBuilderclass.

有没有办法让我序列化Type对象?请注意,我不是要序列化它StringBuilder本身,而是要序列化Type包含有关StringBuilder该类的元数据的对象。

采纳答案by Brian Sullivan

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:

我不知道可以只使用包含完全限定名称的字符串创建 Type 对象。要获取完全限定名称,您可以使用以下命令:

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.

如果您检查 o.GetType() 的值,它将是 StringBuilder,正如您所期望的那样。

回答by AdamSane

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.

只是看了一下它的定义,并没有标记为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)]

回答by rjzii

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.

根据 System.Type [1] 的 MSDN 文档,您应该能够序列化 System.Type 对象。但是,由于错误显式引用 System.Text.StringBuilder,这可能是导致序列化错误的类。

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

[1] 类型类(系统)- http://msdn.microsoft.com/en-us/library/system.type.aspx

回答by hypehuman

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.

我遇到了同样的问题,我的解决方案是创建一个 SerializableType 类。它可以自由地与 System.Type 相互转换,但它会序列化为字符串。您所要做的就是将变量声明为 SerializableType,然后您可以将其称为 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

您也可以考虑使用 AssemblyQualifiedName 而不是 Type.FullName - 请参阅@GreyCloud 的评论

回答by Dzyann

Brian'sanswer 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 AssemblyQualifiedNameas GreyCloud also pointed out.

如果类型与调用位于同一个程序集中,Brian 的回答会很好地工作(就像 GreyCloud 在其中一条评论中指出的那样)。因此,如果类型在另一个程序集中,您需要使用AssemblyQualifiedName正如 GreyCloud 也指出的那样。

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

但是,当AssemblyQualifiedName保存版本时,如果您的程序集的版本与您拥有该类型的字符串中的版本不同,则它将不起作用。

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

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

I am posting this in case anyone needs it.

我张贴这个以防万一有人需要它。

回答by Peter Riesz

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

我在尝试在 .net 标准 2.0 中进行二进制序列化时遇到了这个问题。我最终使用自定义SurrogateSelectorSerializationBinder.

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

TypeSerializationBinder是必需的,因为框架遇到了麻烦,解决System.RuntimeType它得到前SurrogateSelector。我真的不明白为什么必须在这一步之前解决类型...

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