代码之家  ›  专栏  ›  技术社区  ›  Oliver

如果一个接口属性在一个类中被反序列化,如何定义取哪个类?

  •  4
  • Oliver  · 技术社区  · 14 年前

    想象一下你有下面的课

    [DataContract]
    public class NamedList
    {
        [DataMember]
        public string Name { get; set; }
    
        [DataMember]
        public IList<string> Items { get; private set; }
    
        public DumpList(string name)
        {
            Name = name;
            Items = new List<string>();
        }
    }
    

    如果您将它序列化为一个文件,这很容易,因为 IList 是已知的,可以序列化。

    但是,如果您试图将这个文件反序列化回内存,会发生什么呢?
    它的工作没有任何直接的错误发生。

    如果您试图从列表中添加或删除某些内容,问题就会出现。在这种情况下,你会得到一个例外。这个异常的根源来自于反序列化对象用作IList数组的具体实现的情况。

    在这个简单的例子中,要避免这个问题很容易。只需序列化具体的后备存储,而不是公共属性,并在构造函数中进行更改:

    [DataMember(Name = "Items")]
    private List<string> _Items;
    
    public IList<string> Items
    {
        get
        {
            return _Items;
        }
    }
    
    public DumpList(string name)
    {
        Name = name;
        _Items = new List<string>();
    }
    

    但更有趣的问题是:

    • 为什么选择数组类型的反序列化器作为IList接口的具体实现?
    • 是否可以更改设置?每个接口应采用哪个类?
    • 如果我有一个自定义的接口和该接口的几个实现,是否可以告诉反序列化程序对于给定的接口应该采用哪个具体类?
    2 回复  |  直到 13 年前
        1
  •  1
  •   Lucero    14 年前

    如果您使用 NetDataContractSerializer 它与序列化对象一起存储类型信息,您的问题应该得到解决。但是,它同时降低了对非.NET客户机的互操作性。

        2
  •  3
  •   sinelaw    13 年前

    你可以解决这个问题 使用DataContractSurrogate进行反序列化,该反序列化将IList替换为List。

    public class CustomDataContractSurrogate : IDataContractSurrogate
    {
        // The only function you should care about here. The rest don't do anything, just default behavior.
        public Type GetDataContractType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(ICollection<>)))
            {
                return (typeof(List<>).MakeGenericType(type.GetGenericArguments().Single()));
            }
            return type;
        }
    
        public object GetObjectToSerialize(object obj, Type targetType)
        {
            return obj;
        }
    
        public object GetDeserializedObject(object obj, Type targetType)
        {
            return obj;
        }
    
        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        {
            return null;
        }
    
        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            return null;
        }
    
        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
        }
    
        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }
    
        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        {
            return typeDeclaration;
        }
    }
    

    基本上就是这样,您只需使用该代理项创建DataContractSerializer实例,并将其用于反序列化(对于序列化来说,这并不重要),例如:

    var serializer = new DataContractSerializer(type, new Type[]{}, Int32.MaxValue, false, true, new CustomDataContractSurrogate());
    

    或者采用代理的任何其他构造函数。

    ,(作为答案的补充)如果您使用app/web.config定义的服务,则可以定义自定义行为,该行为使用上述代理创建数据协定序列化程序:

    public class CustomDataContractSerializerBehavior : DataContractSerializerOperationBehavior
    {
        public CustomDataContractSerializerBehavior(OperationDescription operation)
            : base(operation)
        {
        }
    
        public CustomDataContractSerializerBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
            : base(operation, dataContractFormatAttribute)
        {
        }
    
        public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns,
            IList<Type> knownTypes)
        {
            return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate());
        }
    
        public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name,
            XmlDictionaryString ns, IList<Type> knownTypes)
        {
            return new DataContractSerializer(type, knownTypes, Int32.MaxValue, false, true, new CustomDataContractSurrogate());
        }
    
    }
    

    终于 您可以使用此行为:

    public static IMyDataServiceContract CreateService()
    {
        var factory = new ChannelFactory<IMyDataServiceContract>("MyServiceName");
        SetDataContractSerializerBehavior(factory.Endpoint.Contract);
        return factory.CreateChannel();
    }
    
    private static void SetDataContractSerializerBehavior(ContractDescription contractDescription)
    {
        foreach (OperationDescription operation in contractDescription.Operations)
        {
            ReplaceDataContractSerializerOperationBehavior(operation);
        }
    }
    
    private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
    {
        DataContractSerializerOperationBehavior dcsOperationBehavior =
        description.Behaviors.Find<DataContractSerializerOperationBehavior>();
    
        if (dcsOperationBehavior != null)
        {
            description.Behaviors.Remove(dcsOperationBehavior);
            description.Behaviors.Add(new CustomDataContractSerializerBehavior(description));
        }
    }
    

    完成这项工作 ,调用上面的CreateService来创建通道。