我是否应该选择带有或不带有私有字段的属性?


16

我现在使用的代码库具有使用私有字段和公共属性的约定。例如,大多数类的成员定义如下:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

我知道这些可以在没有内部私有属性的情况下被重写:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

我想要一些输入:

  • 是否有充分的理由偏爱较旧,更明确的样式而不是较新,更简洁的样式?
  • 我应该使用简洁样式编写任何新类,还是应该尝试匹配旧代码以保持一致性?在这种情况下,一致性是否足够有价值以证明较旧的格式?


@PeterK。内容丰富。不回答我是否应该担心保持程序其余部分的样式,还是是否有足够小的细节不重要的问题。
KChaloux 2012年

@KChaloux:理解!这就是为什么它是评论而不是答案。:-)
Peter K.

@PeterK。
Fair'nuff

1
保持不变的另一个原因是,是否有任何东西通过WCF传递到网上-自动属性存在合同问题(由于.NET创建的后备字段名称中的无效字符)
Leon

Answers:


16

在某些情况下,仍然需要所谓的“较旧”样式:

答:使用语言提供的不变性的不变类型。readonlyC#中的修饰符在构造后冻结该值。目前还没有办法通过自动实现的属性来模仿它。

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B:您的获取者/设定者有任何逻辑。数据绑定到您的类的WPF和Silverlight(及类似代码)将实现INotifyPropertyChanged如下:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

除此之外,我会说使用新样式。使您的代码简洁明了,并为虫子提供了更小的表面积。此外,如果需要更改以包含逻辑,它就不会与签名不兼容。


2
每个人似乎都同意使用新样式。告诉我有关使用readonly修饰符的字段的必要性(您不知道),您会得到+1的评分,并获得答案。= D
KChaloux 2012年

3
有些人(包括我在内)认为具有私有访问器的自动属性对于不可变属性“足够好”。是的,它们可能并不是真正不变的,但是您只需要注意不要在构造函数之外使用setter。
svick

2
另一个原因是,如果您想传递ref与属性不兼容的值,则需要该字段
stijn 2012年

1
像Fody这样的工具可以实现,INotifyPropertyChanged而无需显式的后备字段。github.com/Fody/PropertyChanged
Sebastian Redl

3
注意:C#6允许“仅获取自动属性”,该属性在一行中提供了我的示例“ A”正在执行的操作。
Jesse C. Slicer,

6

我要说的是,拥有明确的后备字段只是徒劳的:它使您的代码更长,更难阅读。它还增加了潜在的错误:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

我认为这样的错误很容易发生,并且很难发现。

而且,如果要保持一致性,可以用另一种方法做到:将使用后备字段的代码更改为使用自动属性的代码。如果您使用ReSharper之类的重构工具,那么这特别容易(如果您不使用这种工具,我认为应该开始)。

当然,在某些情况下,必须有支持领域,但是我认为这与这个问题无关。


实际上,在重构代码时,我实际上是在一些旧字段上完成的,这很痛苦。
KChaloux 2012年

1

即使这个问题已经很老了,我还是想增加一些要点,这些要点我是从一本书中读到的。

  1. 运行时序列化引擎将文件名保留在序列化流中。自动实现属性(AIP)的后备字段的名称由编译器确定,并且每次重新编译代码时,它实际上都可以更改该后备字段的名称,从而无法反序列化包含以下内容的任何类型的实例的功能: AIP。因此,请勿将AIP与要序列化或反序列化的任何类型一起使用。

  2. 调试时,不能在AIP get或set方法上设置断点,因此无法轻松检测应用程序何时获取或设置此属性。您可以在手动实现的断点上设置断点


3
#1-大多数序列化程序默认情况下会忽略私有属性/字段;我从未遇到您提到的问题。#2-在VS2015中已修复,可以在VS2013中解决。请参阅此Visual Studio Magazine文章
布赖恩

-3

是否有充分的理由偏爱较旧,更明确的样式而不是较新,更简洁的样式?

并不是的。尽管我会争辩说,只要您拥有这样的传递属性,就应该使用公共场所作为起点。

我应该使用简洁样式编写任何新类,还是应该尝试匹配旧代码以保持一致性?在这种情况下,一致性是否足够值得证明较旧的格式?

假设您忽略了上述建议并具有直通属性,那么我将不会匹配该样式。理想情况下,您应该回过头来将旧样式重构为新样式,以保持一致,但是人们误读该代码的机会很小。



@svick-ah。除非您对类型进行反射,否则没有区别。如果要公开将类型作为服务或库接口公开,则将公开一个无论如何都需要属性的接口。OP没有执行任何这些操作。
Telastyn 2012年

2
在这些有限的情况下,没有技术原因。不要忘记,属性在调试器,类图,智能感知中的行为会有所不同,而当您认为自己没有进行反射时,.NET框架就是如此。WinForms,WPF和其他几个组件在内部使用反射,并且它们确实分别处理属性,因此,使用此公共字段的建议永远不会被该站点上的许多开发人员所接受。
凯文·麦考密克

1
@KevinMcCormick,并且该框架在字段方面表现得非常好(据我了解)。它并不真正的问题,因为现在有自动性,但更大的问题是使公众的东西摆在首位。越来越多的人认为,简单地使事物具有某种属性会使它们脱离“不要成为公共场所”。
Telastyn 2012年

2
框架以不同的方式处理它们:尝试在EF POCO中使用公共字段,将其绑定到DataGridView,使用PropertyGrid,等等。我只是在反编译器中对Type.GetProperties / GetProperty进行了快速搜索,框架对该方法进行了调用数百次,但没有到GetField。底线是不同的,始终将属性用于公共数据的建议部分基于此事实。这并不排除使用字段,但是这种情况需要证明。但是,您的另一点绝对是正确的,不小心公开数据总是一个坏主意。
凯文·麦考密克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.