代码之家  ›  专栏  ›  技术社区  ›  Jay Bazuzi Buck Hodges

琐碎的财产曾经拯救过你的培根吗?

  •  19
  • Jay Bazuzi Buck Hodges  · 技术社区  · 16 年前

    这里有很多建议,您不应该公开地公开您的字段,而应该使用琐碎的属性。我看了一遍又一遍。

    我理解这些论点,但是 I don't think it's good advice in most cases .

    有没有人能举一个真正重要的时代的例子?当写一个微不足道的财产使一些重要的事情在未来成为可能(或当没有使用一个使他们陷入真正的麻烦时)?

    edit:数据绑定参数是正确的,但不是很有趣。这是数据绑定代码中的一个错误,它不接受公共字段。所以,我们必须编写属性来解决这个bug,而不是因为属性是一个明智的类设计选择。

    编辑:说清楚,我在找现实世界的例子,而不是理论。一个真正重要的时刻。

    编辑:在设置器上设置断点的能力似乎很有价值。为调试程序设计代码是不幸的:我希望调试程序变得更智能,但是考虑到我们拥有的调试程序,我将拥有这种能力。好东西。

    11 回复  |  直到 12 年前
        1
  •  25
  •   Robert Paulson    16 年前

    让代码在不确定的未来工作可能很困难,但这并不是懒惰的借口。在字段上对属性进行编码是约定的,而且是实用的。称之为防御编程。

    其他人也会抱怨存在速度问题,但是Jit'er足够聪明,可以让它和公开一个公共领域一样快。足够快,我永远不会注意到。

    想到一些不平凡的事情

    1. 公共字段是完全公共的,不能强制使用只读或只写语义。
    2. 属性可以具有不同的 get 对战 set 可访问性(例如公共获取、内部集)
    3. 不能重写字段,但可以具有虚拟属性。
    4. 你们班对公共领域没有控制权
    5. 您的类可以控制该属性。它可以将设置限制为允许的值范围、更改状态的标志,甚至可以延迟加载该值。
    6. 反射语义不同。公共字段不是属性。
    7. 正如其他人指出的,没有数据绑定。(这对你来说只是一只虫子。-我可以理解为什么.NET框架设计者不支持他们不赞成的模式。)
    8. 不能在接口上放置字段,但可以在接口上放置属性。
    9. 您的财产甚至不需要存储数据。您可以创建一个外观并分派到包含的对象。

    您只需额外输入13个字符来保证正确性。这似乎不像是推测性的概括性。有一个语义上的差异,如果没有其他的区别,一个属性有一个不同的语义意义,比一个公共字段灵活得多。

     public string Name { get; set; }
     public string name;
    

    我记得有一次我第一次使用.NET时,我把一些类编码为字段,然后出于某种原因需要将它们作为属性,而当我第一次就可以正确地完成时,这是完全浪费时间的。

    你有什么理由 遵循惯例?你为什么觉得有必要逆流而上?不这么做是什么拯救了你?

        2
  •  13
  •   JaredPar    16 年前

    我有一个微不足道的属性,在调试时可以节省几次。.NET不支持数据断点(读或写)的概念。在调试非常复杂的场景时,偶尔跟踪对特定属性的读/写是很重要的。这对于一个属性来说很容易,但是对于一个字段来说是不可能的。

    如果您不在生产环境中工作,那么为了调试的目的重构field->属性很简单。偶尔,您会遇到一些只在生产环境中复制的错误,这些错误很难用新的二进制文件进行修补。一个属性可以在这里保存您。

    不过,这是一个相当有限的情况。

        3
  •  12
  •   Joel Coehoorn    16 年前

    我以前也这么想,杰伊。如果一个属性只提供 直接的 访问私人成员?如果你能把它描述成一个自动属性,那么拥有一个属性而不是一个字段似乎有点愚蠢。即使您需要更改实现,您也可以稍后重构为不动产,任何依赖代码仍然可以工作,对吗?嗯,也许不是。

    你看,我最近看到了关于琐碎属性的光,所以也许现在我可以帮助你做同样的事情。

    最后让我信服的是一个相当明显的观点(回顾起来),即.NET中的属性只是getter和setter方法的语法糖,而这些方法具有 别名 从物业本身。同一程序集中的代码仍然可以工作,因为无论如何您都必须同时重新编译它。但是在不同程序集中链接到您的代码 失败 如果您将字段重构为属性,除非它同时根据新版本重新编译。如果这是一个从一开始的财产,一切仍然是好的。

        4
  •  10
  •   Jason Bunting    16 年前

    其中一个想法是,这些属性在将来可能并不琐碎——如果您将外部代码绑定到某个字段,然后希望将其包装到某个属性中,则所有依赖代码都必须更改,并且您可能无法这样做,特别是在您是控件设计器或具有无法控制的库的情况下,等等。

    更不用说,有些.NET实践不允许您特别使用字段-数据绑定。

    我相信还有其他的好理由。为什么这对你很重要?使用自动属性并完成它。我觉得有些事情不值得担心…

        5
  •  10
  •   Jon Skeet    16 年前

    我将用另一个问题回答你的问题:你是否真的受益于不公开你所有的类型和成员?我怀疑这样做并没有直接阻止任何错误。但是,我已经正确地封装了类型,只公开了要公开的内容。性能相似-良好的设计比其他任何东西都重要。我认为属性在概念上不同于字段;它们是合同的一部分,而不是实现的基本部分。将它们看作属性而不是字段有助于我更清楚地思考我的设计,从而得到更好的代码。

    噢,我偶尔也从不破坏源代码兼容性、能够设置断点、日志访问等方面受益。

        6
  •  3
  •   Jeff Yates    16 年前

    如果字段被属性访问器包装,那么调试涉及该字段的问题就容易得多。在访问器中放置断点可以很快帮助查找可重入性和其他可能无法捕获的问题。通过通过访问器对字段的所有访问进行编组,您可以确切地确定是谁在更改什么以及何时更改。

        7
  •  2
  •   Joel Coehoorn    16 年前

    在.NET中,据我所知,您不能将数据绑定到公共字段,而只能绑定到属性。因此,如果您想进行数据绑定,就没有选择。

        8
  •  1
  •   chills42    16 年前

    我曾经有过一些字段,我想从一个允许程序统计(totalitems和successfulitems)的项目窗口中公开这些字段。

    后来,我决定在窗体上显示统计信息,并能够在setter中添加一个调用,该调用在属性更改时更新了显示。

        9
  •  1
  •   to StackOverflow    16 年前

    显然,如果您没有创建共享类库,并且没有使用数据绑定,那么使用字段将不会导致任何问题。

    但是,如果您要创建一个共享类库,那么imho除了遵循指导原则之外,还做其他事情是愚蠢的,原因通常有三个:

    • 共享类库的使用者可能希望使用数据绑定。

    • 共享类的使用者可能需要二进制兼容性,如果从字段切换到属性,这是不可能的。

    • 最不意外的原则是,您应该与包括.NET框架本身在内的其他共享类库保持一致。

        10
  •  0
  •   Daniel Auger    16 年前

    我不知道有什么东西像人们所说的那样微不足道。通过数据绑定之类的工作方式,微软暗示,作为对象公共接口一部分的任何数据位都应该是一个属性。我不认为它们仅仅意味着它是一种约定,就像在其他一些语言中一样,属性语法更多的是关于约定和便利性。

    一个更有趣的问题可能是:“什么时候我应该使用公共领域而不是财产”,或者“什么时候公共领域而不是公共财产拯救了你的培根?”

        11
  •  0
  •   supercat    12 年前

    结构类型的字段允许直接访问其成员,而此类类型的属性则不允许。因此,如果 Thing.Boz 是一个类型的字段 Point ,要修改其 X 价值可以简单地说 Thing.Boz.X += 5; 如果 博兹 如果是可变属性,则必须使用 var tmp = Thing.Boz; tmp.X += 5; Thing.Boz = tmp; . 用暴露的字段更清晰地写东西的能力通常是, 但并不总是 祝福。

    如果它总是可以 Boz 要成为一个字段,直接修改其成员将比将其复制到临时变量、修改临时变量和将其复制回临时变量更干净、更快和更好。如果类型 博兹 直接公开它的可变字段(结构应该这样),而不是用普通包装纸包装它们,它还可以使用 Interlocked 它们的方法——这对于属性来说是不可能的。以这种方式使用字段实际上只有一个缺点:如果需要用属性替换字段,则依赖于字段的代码将中断,并且可能很难修复。

    简言之,我假定,在不需要重新编译任何使用者的情况下,如果人们不关心能够交换不同版本的代码,那么使用属性而不是字段的最大效果是防止代码使用者编写代码,从而利用(并依赖)公开域的语义。属于结构类型的DS。

    顺便说一下,公开结构类型的字段的另一种方法是公开 ActOnXXX 方法。例如:

    delegate void ActionByRef<T1>(ref T1 p1);
    delegate void ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
    delegate void ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);
    // Method within the type that defines property `Boz`
    void ActOnBoz<T1>(ActionByRef<Point, T1> proc, ref T1 p1)
    {
      proc(ref _Boz, ref p1); // _Boz is the private backing field
    }
    

    要添加局部变量的代码 q Thing.Boz.X 可以打电话 Thing.ActOnBoz((ref Point pt, ref int x) => {pt.X += x;}, ref q); 直接执行动作 Thing._Boz 即使田地没有暴露。