使用 WCF 时生成奇怪的引用 class
Strange reference class generation when using WCF
我遇到了一个(我认为是)服务引用生成问题。
原文class(例子)
[Serializable()]
public class Foo
{
private int _Bar;
public int Bar
{
get { return _Bar; }
set { _Bar = value; }
}
public Foo()
{
this._Bar = 42;
}
}
我发现构造函数使用私有支持字段而不是使用 public setter 很奇怪,所以我重构为:
[Serializable()]
public class Foo
{
public int Bar { get; set; }
public Foo()
{
this.Bar = 42;
}
}
我相信这两个看起来足够等价...但是当我重新生成包含对 Foo 的引用的服务引用时...我收到编译错误。
No reference/extension method for _Bar exists in Foo
请注意,这只是我所记得的编译错误,因为这只是我遇到的一般示例。存在依赖于此服务引用的现有代码,它以某种方式引用了 Foo._Bar
- 即使它是私有的。
所以...这是预期的行为吗?我重构的 class 尽管看起来和我一样...以一种我没想到的方式生成了一个参考 class。
我假设是因为在构造函数中直接引用了私有 _Bar
,所以即使它是私有的,它也会以某种方式与 class 序列化?
我很担心这种行为,因为我在我们的代码库中的许多地方进行了类似的重构 - 我是否不了解序列化 classes 的工作原理?
编辑:
我注意到在 Foo
class 上创建的原始参考文件如下所示:
[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
是原始 class 参考文件中的可访问 属性,而不是 _Bar
- 但这个假设在这个方面是不正确的案件。我在这里缺少什么吗?为什么生成参考文件时使用私有 _Bar
作为 属性,而不是 public Bar
作为 getter 和 [=53] =] 用于私有支持字段?
出现此行为是因为您用 Serializable
attribute, but no data contract attributes. According to Types Supported by the Data Contract Serializer、
标记了 class
The following is a complete list of types that can be serialized:
- Types marked with the SerializableAttribute attribute. Many types included in the .NET Framework base class library fall into this category. The DataContractSerializer fully supports this serialization programming model that was used by .NET Framework remoting, the BinaryFormatter, and the SoapFormatter, including support for the ISerializable interface.
那么,这个 "serialization programming model" 是如何工作的?来自 docs:
When you apply the SerializableAttribute
attribute to a type, all private and public fields are serialized by default.
因此,您(无意中)指示数据合同序列化器自动生成一个合同,该合同序列化您 class 的私有和 public 字段,而不是属性。然后,当您将 属性 切换为 auto-implemented, you are changing the name of the its backing field from _Bar
to the name of the 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 会自动生成具有 public 以可序列化数据协定成员命名的属性的数据协定类型。由于这些成员以私有字段命名,这会将服务器上的私有字段名称提升为客户端中的 public 属性 名称,使您的 class [=65= 看似私有的方面].
您有几种方法可以避免此问题:
将可序列化类型提取到 DLL 中,然后 link 将其提取到客户端和服务器中。在这种情况下,自动生成的数据合同无关紧要。
删除 [Serializable]
属性。这样做会导致 DataContractSerializer
推断出序列化 all public fields, and properties with public get and set methods.
的合约
如果无法删除 [Serializable]
,请使用显式数据协定属性注释 class。这些将覆盖自动生成的 Serializable
合约并稳定合约成员名称。
我遇到了一个(我认为是)服务引用生成问题。
原文class(例子)
[Serializable()]
public class Foo
{
private int _Bar;
public int Bar
{
get { return _Bar; }
set { _Bar = value; }
}
public Foo()
{
this._Bar = 42;
}
}
我发现构造函数使用私有支持字段而不是使用 public setter 很奇怪,所以我重构为:
[Serializable()]
public class Foo
{
public int Bar { get; set; }
public Foo()
{
this.Bar = 42;
}
}
我相信这两个看起来足够等价...但是当我重新生成包含对 Foo 的引用的服务引用时...我收到编译错误。
No reference/extension method for _Bar exists in Foo
请注意,这只是我所记得的编译错误,因为这只是我遇到的一般示例。存在依赖于此服务引用的现有代码,它以某种方式引用了 Foo._Bar
- 即使它是私有的。
所以...这是预期的行为吗?我重构的 class 尽管看起来和我一样...以一种我没想到的方式生成了一个参考 class。
我假设是因为在构造函数中直接引用了私有 _Bar
,所以即使它是私有的,它也会以某种方式与 class 序列化?
我很担心这种行为,因为我在我们的代码库中的许多地方进行了类似的重构 - 我是否不了解序列化 classes 的工作原理?
编辑:
我注意到在 Foo
class 上创建的原始参考文件如下所示:
[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
是原始 class 参考文件中的可访问 属性,而不是 _Bar
- 但这个假设在这个方面是不正确的案件。我在这里缺少什么吗?为什么生成参考文件时使用私有 _Bar
作为 属性,而不是 public Bar
作为 getter 和 [=53] =] 用于私有支持字段?
出现此行为是因为您用 Serializable
attribute, but no data contract attributes. According to Types Supported by the Data Contract Serializer、
The following is a complete list of types that can be serialized:
- Types marked with the SerializableAttribute attribute. Many types included in the .NET Framework base class library fall into this category. The DataContractSerializer fully supports this serialization programming model that was used by .NET Framework remoting, the BinaryFormatter, and the SoapFormatter, including support for the ISerializable interface.
那么,这个 "serialization programming model" 是如何工作的?来自 docs:
When you apply the
SerializableAttribute
attribute to a type, all private and public fields are serialized by default.
因此,您(无意中)指示数据合同序列化器自动生成一个合同,该合同序列化您 class 的私有和 public 字段,而不是属性。然后,当您将 属性 切换为 auto-implemented, you are changing the name of the its backing field from _Bar
to the name of the 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 会自动生成具有 public 以可序列化数据协定成员命名的属性的数据协定类型。由于这些成员以私有字段命名,这会将服务器上的私有字段名称提升为客户端中的 public 属性 名称,使您的 class [=65= 看似私有的方面].
您有几种方法可以避免此问题:
将可序列化类型提取到 DLL 中,然后 link 将其提取到客户端和服务器中。在这种情况下,自动生成的数据合同无关紧要。
删除
[Serializable]
属性。这样做会导致DataContractSerializer
推断出序列化 all public fields, and properties with public get and set methods. 的合约
如果无法删除
[Serializable]
,请使用显式数据协定属性注释 class。这些将覆盖自动生成的Serializable
合约并稳定合约成员名称。