c# - 如果在类中反序列化接口(interface)属性,如何定义采用哪个类?

标签 c# interface datacontractserializer

想象一下你有以下类(class)

[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 背后的具体类是已知的并且可以序列化。

但是如果你尝试将这个文件反序列化回内存中会发生什么?
它可以正常工作,不会发生任何直接错误。

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

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

[DataMember(Name = "Items")]
private List<string> _Items;

public IList<string> Items
{
    get
    {
        return _Items;
    }
}

public DumpList(string name)
{
    Name = name;
    _Items = new List<string>();
}

但更有趣的问题是:

  • 为什么选择Array类型的Deserializer作为IList接口(interface)的具体实现?
  • 是否可以更改每个接口(interface)应采用哪个类的设置?
  • 如果我有一个自定义接口(interface)和该接口(interface)的多个实现,是否可以告诉反序列化器应该为给定接口(interface)采用哪个具体类?

最佳答案

您可以解决此问题,使用 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 来创建 channel 。

关于c# - 如果在类中反序列化接口(interface)属性,如何定义采用哪个类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2848503/

相关文章:

c# - 两个流的条件配对 - 在响应式扩展中组合 If

C# 单元测试 NSubstitute 无法在 ObjectCache 中设置值

Java - 接口(interface),实例化一个接口(interface)?

java - 理解 JavaBeans 模式(抽象 getter 和 setter)

c# - 在 WCF 服务中重命名 __type-field

c# - 在 Unity 中使用 Firebase 云消息传递时如何获取当前 Activity ?

c# - ASP.NET Core 使用依赖注入(inject)访问其他服务

c# - 错误 : Does not implement interface member

c# - DataContractJsonSerializer 可以处理循环引用吗?