关于序列化:我可以序列化C#Type对象吗?

关于序列化:我可以序列化C#Type对象吗?

Can I serialize a C# Type object?

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

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

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

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

我有办法序列化Type对象吗? 请注意,我不是在尝试序列化StringBuilder本身,而是在Type对象中包含有关StringBuilder类的元数据。


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

1
string typeName = typeof (StringBuilder).FullName;

然后,您可以根据需要持久化此字符串,然后像这样重构类型:

1
Type t = Type.GetType(typeName);

如果需要创建该类型的实例,则可以执行以下操作:

1
object o = Activator.CreateInstance(t);

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


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

这是课程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 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);
    }
}

以及用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[DataContract]
public class A
{

    ...

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

    ...

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

    ...

}

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


如果类型与调用位于同一程序集中,则Brian的答案会很好用(例如其中一条注释中指出的GreyCloud)。
因此,如果类型在另一个程序集中,则需要使用AssemblyQualifiedName,因为GreyCloud也指出了这一点。

但是,当AssemblyQualifiedName保存版本时,如果程序集的版本与您具有该类型的字符串中的版本不同,则它将无法正常工作。

就我而言,这是一个问题,我这样解决了它:

1
2
3
4
5
string typeName = typeof (MyClass).FullName;

Type type = GetTypeFrom(typeName);

object myInstance = Activator.CreateInstance(type);

GetTypeFrom方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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;
    }

如果有人需要,我会发布此信息。


根据System.Type [1]的MSDN文档,您应该能够序列化System.Type对象。但是,由于该错误明确地指向System.Text.StringBuilder,因此很可能是导致序列化错误的类。

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


仅查看其定义,它就没有标记为Serializable。如果您确实需要将此数据进行序列化,则可能必须将其转换为标记为此类的自定义类。

1
2
3
4
5
6
7
8
9
10
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)]

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

需要TypeSerializationBinder是因为框架在获取SurrogateSelector之前无法解决System.RuntimeType。我真的不明白为什么在此步骤之前必须先解析类型...

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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;
    }
}

用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}


推荐阅读