有什么理由在C#中使用私有属性吗?


245

我刚刚意识到C#属性构造也可以与私有访问修饰符一起使用:

private string Password { get; set; }

尽管从技术上讲这很有趣,但是我无法想象何时使用它,因为私有字段甚至仪式更少

private string _password;

而且我无法想象何时需要内部获取但不设置设置但不设置获取私有字段:

private string Password { get; }

要么

private string Password { set; }

但是也许有一个带有嵌套/继承类的用例,或者一个get / set可能包含逻辑而不是仅仅返回属性的值,尽管我倾向于使属性严格简单,让显式方法执行任何逻辑,例如GetEncodedPassword()

是否有人出于某种原因在C#中使用私有属性,还是仅仅是在技术上实际上尚未实际使用的代码构造之一?

附录

不错的答案,仔细阅读它们,我便剔除了私人财产的这些用途:

  • 当需要延迟加载私有字段时
  • 当专用字段需要额外的逻辑或为计算值时
  • 因为私有字段可能很难调试
  • 为了“向自己出示合同”
  • 在内部转换/简化公开属性作为序列化的一部分
  • 包装全局变量以在您的类中使用

受私有财产鼓励的技术是自我封装 -请参阅:sourcemaking.com/refactoring/self-encapsulate-field
LBushkin,2010年

Answers:


212

如果需要缓存值并想延迟加载它,则可以使用它们。

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
一个不错的通用模式是return _password ?? (_password = CallExpensiveOperation());
Marc

1
如果可以使用@EvAlex Lazy <T>,则可能更可取。但是它只能使用静态内容,因此您无法访问(非静态)方法或其他成员。顺便说一句,我return _password ?? (_password = CallExpensiveOperation());一段时间以来更喜欢单行语法。或者实际上Resharper更喜欢它:)。
巴特2014年

4
@Marc自C#6起,您甚至不必编写get和return。private string Password => _password ?? (_password = CallExpensiveOperation());
奥托(Otto)Abnormalverbraucher

1
使用C#8,它甚至更短:private string Password => _password ??= CallExpensiveOperation();
Dave M

2
@Bas这不是延迟加载,因为CallExpensiveOperation();在包含对象的构造/初始化期间而不是在首次访问该属性时调用。
Stefan Podskubka,

142

正如其他人所提到的,此代码在我的代码中的主要用法是延迟初始化。

在字段上使用私有属性的另一个原因是,私有属性比私有字段容易调试得多。我经常想知道诸如“此字段被意外设置;谁是设置此字段的第一个呼叫者?”之类的信息。如果您可以在setter上放置一个断点并按下go键,就会更容易。您可以在其中登录。您可以在其中放置性能指标。您可以进行在调试版本中运行的一致性检查。

基本上可以归结为:代码远比数据强大。任何能让我编写所需代码的技术都是不错的选择。字段不允许您在其中编写代码,而属性则允许。


5
您说的是“代码比数据强大得多”吗?谷歌搜索返回指向您的引用。只是想知道,以便在需要时可以正确引用。
Joan Venge

24
@琼:我不知道。我要么编造出来,要么听别人说,然后想:“哇,我应该完全偷走它,然后再把我从谁那里偷走的事都忘了。”
埃里克·利珀特

1
+ CodeLens告诉您在哪里引用了属性
UuDdLrLrSs

43

也许有一个带有嵌套/继承类的用例,或者一个get / set可能包含逻辑而不是仅仅返回属性的值

即使我不需要属性的getter或setter方法上的逻辑,我也亲自使用它。使用一个属性,甚至是一个私有属性,都可以帮助您对代码进行过时验证,以便以后可以根据需要将逻辑添加到吸气剂中。

如果我觉得某个属性最终可能需要额外的逻辑,则有时我会将其包装到一个私有属性中,而不是使用字段,只是这样,以后就不必更改代码了。


在半关联的情况下(尽管与您的问题有所不同),我经常在公共财产上使用私有设置者:

public string Password 
{
    get; 
    private set;
}

这为您提供了一个公共获取者,但是却使设置者不公开。


+1是有道理的:“如果我觉得某个属性最终可能需要额外的逻辑,则有时我会将其包装到一个私有属性中,而不是使用字段,只是这样我以后就不必更改代码了。”
爱德华·坦格伊

7
私人
传球手

20

私有获取属性的一种很好用法是计算值。几次我都拥有私有只读属性,只是对我类型中的其他字段进行了计算。它不值得使用一种方法,对于其他类也不是那么有趣,所以它是私有属性。


20

延迟初始化是它们可以整洁的地方,例如

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

然后,您可以编写:this.MyType在任何地方而不是this.mytype.Value将其延迟实例化在一个地方的事实加以封装。

令人遗憾的是,C#不支持对属性的后备字段进行范围界定(即在属性定义中声明它)以完全隐藏该字段并确保只能通过该属性进行访问。


2
同意在那里具有范围界定方面。
克里斯·马里西奇

5
我经常使用相同的技术,并且我也希望字段可以限定为代码主体。这是一个不错的功能,但是优先级较低。
埃里克·利珀特

5
@Eric利珀特- field-declarationS中的作用域accessor-declarations已排名第1我的C#的愿望清单上很长一段时间。如果您能在某个(实际)将来的版本中获得设计和实现的功能,那么我将为您锦上添花。
杰弗里·惠特里奇

13

我能想到的唯一一种用法

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

+1是从其他私有变量计算得出的有用的属性类别
Daren Thomas,2010年

2
为什么不使用私有方法private bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

属性和字段不是一对一的。属性与类的接口有关(无论是谈论其公共接口还是内部接口),而字段则与类的实现有关。属性不应视为仅公开字段的方式,而应视为公开类的意图和目的的方式。

就像您使用属性向消费者介绍构成班级的合同一样,您也可以出于非常相似的原因向自己提出合同。是的,我会在有意义的时候使用私有属性。有时,私有属性可以隐藏诸如延迟加载之类的实现细节,事实上,该属性实际上是多个字段和方面的集合,或者每个调用都需要虚拟化该属性(请考虑DateTime.Now)。肯定有时候,即使在类的后端对自己实施此操作也是有意义的。


+1:“您也可以出于类似的原因向自己出示合同”,这很有道理
Edward Tanguay 2010年

8

我在序列化中使用它们,DataContractSerializer或者使用诸如protobuf-net之类的东西来支持这种用法(XmlSerializer不)。如果您需要简化对象作为序列化的一部分,这将很有用:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

我一直做的一件事是将“全局”变量/缓存存储到 HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

我不时使用它们。当您可以轻松地在属性中放置断点或添加日志记录语句等时,它们可以使调试变得更容易。

如果以后需要以某种方式更改数据类型或需要使用反射时,也可能很有用。


同上; 如果在获取/设置中涉及逻辑,我有时可能会使用私有或受保护的属性。通常取决于多少逻辑:我将在属性中执行简单的逻辑,通常将使用辅助函数的许多逻辑。无论什么使代码最易于维护。
TechNeilogy

5

我使用私有属性来减少用于访问经常使用的子属性的代码。

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

如果有许多子属性,这将很有用。


2

通常只使用get / set方法修改成员的方法,甚至私有方法也是如此。现在,这背后的逻辑是让您知道您的get / set总是以特定的方式运行(例如触发事件),这似乎没有任何意义,因为那些不会包含在属性方案中...但是旧习惯很难改掉。


2

当存在与属性set或get(认为是延迟初始化)关联的逻辑并且该属性在类中的几个地方使用时,这是非常有意义的。

如果这只是一个直接的支持领域?没有什么可以想到的。


2

我知道这个问题很老,但以下任何当前答案中都没有下面的信息。

我无法想象何时需要内部获取但不固定

如果要注入依赖项,则您可能希望在属性上有一个Getter而不是setter,因为这将表示一个只读属性。换句话说,该属性只能在构造函数中设置,而不能由该类中的任何其他代码更改。

此外,Visual Studio Professional将提供有关属性而不是字段的信息,从而使您更容易查看正在使用的字段。

猪场


1

好吧,正如没有人提到的那样,您可以使用它来验证数据或锁定变量。

  • 验证方式

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • 锁定

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    注意:锁定引用时,您不会锁定对引用对象成员的访问。

延迟参考:延迟加载时,您可能最终需要异步执行,而现在有AsyncLazy。如果您使用的版本比Visual Studio SDK 2015的版本旧,或者不使用它,则还可以使用AsyncEx的AsyncLazy


0

显式字段的一些更特殊的用途包括:

  • 您需要使用refout使用其值-可能是因为它是一个Interlocked计数器
  • 旨在表示基本布局,例如struct使用显式布局(可能映射到C ++转储或unsafe代码)
  • 历史上,该类型已BinaryFormatter与自动字段处理一起使用(更改为auto-props会更改名称,从而破坏序列化程序)
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.