【问题标题】:Strange reference class generation when using WCF使用 WCF 时生成奇怪的引用类
【发布时间】:2023-03-06 03:35:01
【问题描述】:

我遇到了一个(我认为是)服务引用生成问题。

原始类(示例)

[Serializable()]
public class Foo
{
    private int _Bar;
    public int Bar
    {
        get { return _Bar; }
        set { _Bar = value; }
    }

    public Foo()
    {
        this._Bar = 42;
    }
}

我发现构造函数使用私有支持字段而不是使用公共设置器很奇怪,所以我重构了这个:

[Serializable()]
public class Foo
{
    public int Bar { get; set; }

    public Foo()
    {
        this.Bar = 42;
    }
}

我相信这两个似乎足够等效...但是当我重新生成包含对 Foo 的引用的服务引用时...我收到了编译错误。

Foo 中不存在 _Bar 的引用/扩展方法

请注意,这只是我对编译错误的记忆,因为这只是我遇到的一般示例。存在依赖此服务引用的现有代码,它以某种方式引用了 Foo._Bar - 即使它是私有的。

那么...这是预期的行为吗?我重构的类虽然看起来和我一样……以我没想到的方式生成了一个参考类。

我假设因为私有 _Bar 在构造函数中被直接引用,所以即使它是私有的,它也以某种方式与类一起序列化?

我很担心这种行为,因为我在代码库的许多地方都进行了类似的重构 - 我不了解序列化类的工作原理吗?

编辑:

我注意到在 Foo 类上创建的原始参考文件如下所示:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Foo", Namespace="http://schemas.datacontract.org/2004/07/Foo")]
[System.SerializableAttribute()]
public partial class Foo: object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

    private int _BarField;

    [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
    public int _Bar {
        get {
            return this._BarField;
        }
        set {
            if ((this._BarField.Equals(value) != true)) {
                this._BarField = value;
                this.RaisePropertyChanged("_Bar");
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName) {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null)) {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

我想我希望 Bar 是原始类的参考文件中的可访问属性,而不是 _Bar - 但在这种情况下,这种假设是不正确的。我在这里缺少什么吗?为什么生成参考文件时使用私有_Bar 作为属性,而不是使用公共Bar 作为私有支持字段的getter 和setter?

【问题讨论】:

  • 您是否正在编辑生成的代码并且是partial 类?也许类的另一部分是指原始的_Bar
  • @MickyDuncan 不是部分类,我没有编辑自动生成的代码 :(

标签: c# asp.net wcf serialization


【解决方案1】:

出现此行为是因为您使用Serializable 属性标记了您的类,但没有data contract attributes。根据Types Supported by the Data Contract Serializer

以下是可以序列化的类型的完整列表:

  • 用 SerializableAttribute 属性标记的类型。 .NET Framework 基类库中包含的许多类型都属于这一类。 DataContractSerializer 完全支持 .NET Framework 远程处理、BinaryFormatter 和 SoapFormatter 使用的这种序列化编程模型,包括对 ISerializable 接口的支持。

那么,这种“序列化编程模型”是如何工作的呢?来自docs

当您将SerializableAttribute 属性应用于一个类型时,所有私有和公共字段都默认被序列化。

因此,您(无意中)指示数据合同序列化程序自动生成一个合同,该合同序列化您的类的私有和公共字段,而不是属性。然后,当您将属性切换为auto-implemented 时,您将其支持字段的名称从_Bar 更改为hidden backing field 的名称。反过来,从类型推断的合同包含一个重命名的成员。当您序列化为 XML 时,您可以看到合同中的变化。这是在原始 XML 中序列化的原始字段:

<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question29495337.V1">
    <_Bar>42</_Bar>
</Foo>

以及新 XML 中的支持字段:

<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Question29495337.V2">
    <_x003C_Bar_x003E_k__BackingField>42</_x003C_Bar_x003E_k__BackingField>
</Foo>

然后,当您在客户端中执行 add service reference 时,Visual Studio 会自动生成具有以可序列化数据协定成员命名的公共属性的数据协定类型。由于这些成员以私有字段命名,这会将服务器上的私有字段名称提升为客户端中的公共属性名称,从而使您的类的看似私有的方面公开。

你有几种方法可以避免这个问题:

  1. 将可序列化类型提取到 DLL 中并将其链接到客户端和服务器。在这种情况下,自动生成的数据合同无关紧要。

  2. 删除[Serializable] 属性。这样做会导致DataContractSerializer 推断出序列化all public fields, and properties with public get and set methods 的合约。

  3. 如果您无法删除 [Serializable],请使用显式数据协定属性注释该类。这些将覆盖自动生成的Serializable 合约并稳定合约成员名称。

【讨论】:

  • 很有意义,感谢您的详细回答:)
猜你喜欢
  • 2011-05-26
  • 1970-01-01
  • 2014-05-25
  • 1970-01-01
  • 2019-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多