使用自动实现属性而不是手动实现属性的任何理由
本文关键字:属性 实现 理由 任何 | 更新日期: 2023-09-27 18:14:20
我理解PROPERTIES相对于FIELDS的优势,但我觉得使用自动实现的属性而不是手动实现的属性,除了使代码看起来更简洁之外,并没有真正提供任何优势。
我觉得用
更舒服 private string _postalCode;
public string PostalCode
{
get { return _postalCode; }
set { _postalCode = value; }
}
代替:
public string PostalCode { get; set; }
主要是因为如果我想做任何类型的get和set的自定义实现,我必须创建我自己的属性,由私有字段支持。那么,为什么不从一开始就咬紧牙关,直接为所有属性提供这种灵活性,以保持一致性呢?考虑到你在Visual Studio中所要做的只是单击你的私有字段名,然后按Ctrl+E,你就完成了,这真的不需要花费额外的一秒钟。如果我手动执行,那么我最终会遇到不一致的情况,其中有一些手动创建的公共属性由私有字段支持,以及一些自动实现的属性。我感觉好多了,所有的一致,无论是自动还是手动。
只有我这样吗?我错过什么了吗?我搞错了什么吗?我是不是太强调一致性了?我总能找到关于c#特性的合理讨论,而且几乎所有事情都有正反两方面,但在这种情况下,我真的找不到任何人建议反对使用自动实现属性。
除了简洁之外,它没有给你任何额外的东西。如果您喜欢更详细的语法,那么无论如何,使用它。
使用auto props的一个优点是,它可以潜在地避免犯愚蠢的编码错误,例如意外地将错误的私有变量赋值给属性。相信我,我以前做过!
你关于自动道具不是很灵活的观点很好。唯一的灵活性是使用private get
或private set
来限制作用域。如果你的getter或setter有任何复杂性,那么auto props就不再是一个可行的选择。
自动实现的属性不保证在构建之间保持相同的支持字段名称。因此,理论上有可能在程序集的一个版本中序列化一个对象,然后在另一个程序集中重新序列化该对象,从而导致破坏性更改。
这是非常不太可能的,但如果您试图保持将程序集的版本"换出"为新版本的能力,则这是一个有效的关注。
通过使用手动实现的属性,您可以保证后备字段永远不会更改(除非您专门更改它)。
除了这个细微的差别之外,auto-property是一个普通的属性,通过一个后备字段自动实现。
您将失去控制的一件事是将后备字段指定为NonSerialized的能力,但是在这种情况下,为该属性创建后备字段非常容易。
忘记了:如果您或您使用的任何产品对成员执行反射(即WCF),那么您将看到混乱的后备字段名称,而不是您创建的"漂亮"后备字段。
如果您之前提供了对服务的访问,或者如果您在接收端反序列化到相同的类结构(即在WCF管道的两端使用相同的类),这可能非常重要。在这种情况下,您不一定能够反序列化,因为您可以保证后备字段名称相同,除非您共享与源代码相反的相同DLL。
稍微澄清一下:假设你有一个web服务,它通过WCF向你创建的silverlight客户端公开了一些业务对象。为了重用业务逻辑,Silverlight客户端会向业务对象的源代码添加引用。如果您有自动实现的属性,则无法控制后备字段名称。由于WCF序列化的是成员而不是属性,因此您无法确保从WCF服务传输到silverlight的对象将正确地反序列化,因为支持字段名称几乎肯定是不匹配的。
有些人认为自动属性可能有点邪恶,但除此之外,它们只是语法糖。使用它们除了节省几行代码之外,您不会获得任何好处,而且您可能会为自己创建更多的工作(由于您想要执行一些检查或引发事件,因此稍后必须手动实现它)。一致性在编程中是很有价值的。
我不知道其他人是怎么想的,但我倾向于停下来思考一下我应该如何命名我的变量和函数,以便其他人能够理解我的代码。
所以当我使用auto实现属性时,我只需要暂停一次。
当我需要一个后备字段时,我必须暂停两次,所以它会减慢开发速度:)
我的做法是:
- 在开始 中设置为私有变量
- 如果需要,将其更改为公共自动实现。
- 如果我需要get或set中的一些代码,请将其更改为backing字段。
如果一个类的不同属性以不同的方式公开,没有什么问题。
我看到使用auto属性的一个优点是;当调试应用程序时,它不会进入不必要的Get/Set部分。我知道我们可以避免使用相同的调试器属性或步骤;但是,如果在大型应用程序上进行调试,大多数情况下都会发生这种情况。
我总能找到关于c#特性的合理讨论,而且几乎每件事都有利弊,但在这种情况下,我真的找不到任何反对使用自动实现属性的人。
我在今天的代码审查中遇到了这个问题,当我向我的同行小组询问时,我们也无法达成共识。我不喜欢模棱两可,我想知道至少一个能回答你问题的紧迫问题:
- 是否有性能增益或击中使用一种方式比另一种?
就像找出需要多少次舔才能到达Tootsie Roll Tootsie Pop的中心一样,我决定"让我们找出答案"。
让我们首先做一个苹果与苹果的比较。
假设有两个类:
public class C
{
private int z;
public int Z
{
get { return z;}
}
}
public class Q
{
public int Z { get; }
}
第一个类是手动备份存储,第二个类是自动编译器生成的版本。
让我们看一下为每一个生成的IL。
首先,手动备份存储版本:
// Fields
.field private int32 z
// Methods
.method public hidebysig specialname
instance int32 get_Z () cil managed
{
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.maxstack 1
.locals init (
[0] int32
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 C::z
IL_0007: stloc.0
IL_0008: br.s IL_000a
IL_000a: ldloc.0
IL_000b: ret
} // end of method C::get_Z
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2068
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
// Properties
.property instance int32 Z()
{
.get instance int32 C::get_Z()
}
现在让我们看看第二个类的IL:
// Fields
.field private initonly int32 '<Z>k__BackingField'
.custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
.custom instance void [System.Private.CoreLib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Private.CoreLib]System.Diagnostics.DebuggerBrowsableState) = (
01 00 00 00 00 00 00 00
)
// Methods
.method public hidebysig specialname
instance int32 get_Z () cil managed
{
.custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2071
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 Q::'<Z>k__BackingField'
IL_0006: ret
} // end of method Q::get_Z
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2068
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Q::.ctor
// Properties
.property instance int32 Z()
{
.get instance int32 Q::get_Z()
}
忽略编译器为添加可调试属性而生成的额外代码,这些代码不会添加任何可执行代码,因此在生成的代码中似乎没有任何差异。
现在,你可能会争辩说你的问题没有得到回答,但是考虑一下…
如果您曾经编写过参与绑定的属性,例如:
private string name;
public string Name
{
get { return name; }
set { SetProperty (ref name, value);
}
那么后备存储是可行的。
另一方面,在Visual Studio编辑器中使用"prop"
所以最后,对于锤子来说,一切看起来都像钉子。别当锤子。