我遇到了(我认为是)服务引用生成的问题。
原始课程(示例)
[Serializable()]
public class Foo
{
private int _Bar;
public int Bar
{
get { return _Bar; }
set { _Bar = value; }
}
public Foo()
{
this._Bar = 42;
}
}
我发现构造函数使用私有支持字段而不是使用公共setter很奇怪,所以我重构了这个:
[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
成为来自原始类的Reference文件中的可访问属性,而不是_Bar
- 但在这种情况下该假设是不正确的。我有什么东西在这里失踪吗?为什么使用私有_Bar
作为属性生成引用文件,而不是用作私有支持字段的getter和setter的public Bar
?
答案 0 :(得分:1)
出现这种情况是因为您使用Serializable
属性标记了您的类,但没有data contract attributes。根据{{3}},
以下是可序列化的完整类型列表:
- 使用SerializableAttribute属性标记的类型。 .NET Framework基类库中包含的许多类型都属于此类别。 DataContractSerializer完全支持.NET Framework远程处理,BinaryFormatter和SoapFormatter使用的此序列化编程模型,包括对ISerializable接口的支持。
那么,这个“序列化编程模型”如何工作?来自Types Supported by the Data Contract Serializer:
将
SerializableAttribute
属性应用于某个类型时,默认情况下会序列化所有私有和公共字段。
因此,您(无意中)指示数据合同序列化程序自动生成序列化类的私有和公共字段的合同,而不是属性。然后,当您将属性切换为docs时,您将其支持字段的名称从_Bar
更改为auto-implemented的名称。反过来,从类型推断出的合同包含一个重命名的成员。序列化为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>
然后,当您在客户端中执行hidden backing field时,Visual Studio会自动生成一个数据协定类型,其中包含以可序列化数据协定成员命名的公共属性。由于这些成员以私有字段命名,因此将服务器上的私有字段名称提升为客户端中的公共属性名称,从而使您的类看似私密的方面公开。
您有几种方法可以避免此问题:
将可序列化类型解压缩到DLL中,并将其链接到客户端和服务器。在这种情况下,自动生成的数据合同无关紧要。
删除 [Serializable]
属性。这样做会导致DataContractSerializer
推断出序列化add service reference的合同。
如果无法删除[Serializable]
,请使用显式数据协定属性注释该类。这些将覆盖自动生成的Serializable
合同并稳定合同成员名称。