如何通过自定义序列化在WCF中传输System.Type?
在WCF中通过自定义序列化传输System.Type的可行方案
你的设想完全站得住脚——通过AssemblyQualifiedName把System.Type转成字符串序列化,接收端再用Type.GetType()还原回Type,这是WCF里处理这类不可直接序列化CLR类型的常用思路,完全可以落地。
具体实现思路
我给你两种常见的实现方式,你可以根据自己的项目情况选择:
方式一:用包装类+隐式转换(简单直观)
先写一个专门的包装类,用来承载Type的字符串表示,同时通过隐式转换让业务代码几乎不用修改:
[DataContract] public class TypeWrapper { [DataMember] public string AssemblyQualifiedName { get; set; } // 从包装类转成Type public static implicit operator Type(TypeWrapper wrapper) { return wrapper == null ? null : Type.GetType(wrapper.AssemblyQualifiedName, true); } // 从Type转成包装类 public static implicit operator TypeWrapper(Type type) { return type == null ? null : new TypeWrapper { AssemblyQualifiedName = type.AssemblyQualifiedName }; } }
之后在你的WCF服务契约里,把原来用Type的参数/返回值换成TypeWrapper就行,业务代码里赋值或者使用的时候,隐式转换会自动帮你完成类型转换,几乎不用额外改动。
方式二:用IDataContractSurrogate(无需修改契约类型)
如果你不想改动原有的服务契约(比如已经定义好的契约里就是用Type),可以用WCF的IDataContractSurrogate来做序列化替换,相当于在序列化/反序列化的过程中偷偷把Type换成字符串:
public class TypeSerializationSurrogate : IDataContractSurrogate { // 告诉序列化器,遇到Type时用string来序列化 public Type GetDataContractType(Type type) { return type == typeof(Type) ? typeof(string) : type; } // 反序列化时把字符串转回Type public object GetDeserializedObject(object obj, Type targetType) { if (targetType == typeof(Type) && obj is string typeFullName) { // 第二个参数设为true,找不到类型时会抛出异常,方便排查问题 return Type.GetType(typeFullName, true); } return obj; } // 序列化时把Type转成AssemblyQualifiedName字符串 public object GetObjectToSerialize(object obj, Type targetType) { if (obj is Type type) { return type.AssemblyQualifiedName; } return obj; } // 下面这些接口方法返回默认值即可 public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) => null; public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes) { } public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) => null; public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit) => typeDeclaration; }
然后需要在服务端和客户端的WCF配置里(或者代码里)注册这个Surrogate,比如用代码配置的话:
// 针对服务端的端点行为 foreach (var endpoint in host.Description.Endpoints) { foreach (var operation in endpoint.Contract.Operations) { var behavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); if (behavior == null) { behavior = new DataContractSerializerOperationBehavior(operation); operation.Behaviors.Add(behavior); } behavior.DataContractSurrogate = new TypeSerializationSurrogate(); } }
客户端的配置逻辑类似,也要添加这个Surrogate。
注意事项
- 程序集一致性:
AssemblyQualifiedName包含了程序集的名称、版本、公钥等信息,所以要保证服务端和客户端引用的相关程序集完全一致,否则Type.GetType()会找不到类型或者抛出异常。 - 空值处理:不管用哪种方式,都要处理
Type为null的情况,避免空引用异常。 - 泛型类型兼容:不用担心泛型类型,
AssemblyQualifiedName对泛型的表示是标准的,Type.GetType()可以正确还原泛型类型。
总的来说,你的思路完全没问题,这两种方式都能稳定实现需求。
内容的提问来源于stack exchange,提问作者Thypari
相关产品推荐
相关产品推荐

