使用自动实现属性而不是手动实现属性的任何理由

本文关键字:属性 实现 理由 任何 | 更新日期: 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 getprivate set来限制作用域。如果你的getter或setter有任何复杂性,那么auto props就不再是一个可行的选择。

自动实现的属性不保证在构建之间保持相同的支持字段名称。因此,理论上有可能在程序集的一个版本中序列化一个对象,然后在另一个程序集中重新序列化该对象,从而导致破坏性更改。

这是非常不太可能的,但如果您试图保持将程序集的版本"换出"为新版本的能力,则这是一个有效的关注。

通过使用手动实现的属性,您可以保证后备字段永远不会更改(除非您专门更改它)。

除了这个细微的差别之外,auto-property是一个普通的属性,通过一个后备字段自动实现。

您将失去控制的一件事是将后备字段指定为NonSerialized的能力,但是在这种情况下,为该属性创建后备字段非常容易。

忘记了:如果您或您使用的任何产品对成员执行反射(即WCF),那么您将看到混乱的后备字段名称,而不是您创建的"漂亮"后备字段。

如果您之前提供了对服务的访问,或者如果您在接收端反序列化到相同的类结构(即在WCF管道的两端使用相同的类),这可能非常重要。在这种情况下,您不一定能够反序列化,因为您可以保证后备字段名称相同,除非您共享与源代码相反的相同DLL。

稍微澄清一下:假设你有一个web服务,它通过WCF向你创建的silverlight客户端公开了一些业务对象。为了重用业务逻辑,Silverlight客户端会向业务对象的源代码添加引用。如果您有自动实现的属性,则无法控制后备字段名称。由于WCF序列化的是成员而不是属性,因此您无法确保从WCF服务传输到silverlight的对象将正确地反序列化,因为支持字段名称几乎肯定是不匹配的。

有些人认为自动属性可能有点邪恶,但除此之外,它们只是语法糖。使用它们除了节省几行代码之外,您不会获得任何好处,而且您可能会为自己创建更多的工作(由于您想要执行一些检查或引发事件,因此稍后必须手动实现它)。一致性在编程中是很有价值的。

我不知道其他人是怎么想的,但我倾向于停下来思考一下我应该如何命名我的变量函数,以便其他人能够理解我的代码。

所以当我使用auto实现属性时,我只需要暂停一次

当我需要一个后备字段时,我必须暂停两次,所以它会减慢开发速度:)

我的做法是:

  1. 在开始
  2. 中设置为私有变量
  3. 如果需要,将其更改为公共自动实现。
  4. 如果我需要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"快捷方式来生成自动属性非常方便,而且比所有 那键入;)

要快得多

所以最后,对于锤子来说,一切看起来都像钉子。别当锤子。