297

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

private string Password { get; set; }

虽然这在技术上很有趣,但我无法想象我什么时候会使用它,因为私有领域涉及的仪式更少

private string _password;

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

private string Password { get; }

或者

private string Password { set; }

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

是否有人出于任何原因在 C# 中使用私有属性,或者它只是那些技术上可能但很少在实际代码中使用的构造之一?

附录

很好的答案,通读它们,我挑选了私有财产的这些用途:

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

19 回答 19

247

如果我需要缓存一个值并想要延迟加载它,我会使用它们。

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

        return _password;
    }
}
于 2010-07-22T15:05:41.757 回答
161

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

私有属性优于字段的另一个原因是私有属性比私有字段更容易调试。我经常想知道诸如“这个字段被意外设置;谁是第一个设置这个字段的调用者?” 如果你可以在设置器上放置一个断点并点击 go,那就更容易了。你可以在那里登录。您可以将性能指标放在那里。您可以进行在调试版本中运行的一致性检查。

基本上,归结为:代码比数据强大得多。任何能让我编写所需代码的技术都是一种很好的技术。字段不允许您在其中编写代码,属性可以。

于 2010-07-22T15:16:34.403 回答
46

可能存在嵌套/继承类的用例,或者 get/set 可能包含逻辑而不是仅仅返回属性的值

即使我不需要属性的 getter 或 setter 的逻辑,我个人也会使用它。使用属性,即使是私有属性,确实有助于您的代码面向未来,以便您可以在以后将逻辑添加到 getter(如果需要)。

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


在一个半相关的情况下(尽管与您的问题不同),我经常在公共属性上使用私有设置器:

public string Password 
{
    get; 
    private set;
}

这为您提供了一个公共 getter,但将 setter 保持为私有。

于 2010-07-22T14:56:05.130 回答
26

私有获取属性的一种很好的用法是计算值。有几次我有私有只读属性,只是对我类型中的其他字段进行计算。它不值得一个方法,对其他类也不感兴趣,所以它是私有财产。

于 2010-07-22T15:06:12.847 回答
21

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

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# 不支持将支持字段的范围限定为属性(即在属性定义中声明它)以完全隐藏它并确保它只能通过属性访问。

于 2010-07-22T15:01:49.057 回答
15

我能想到的唯一一种用法

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}
于 2010-07-22T14:59:50.590 回答
13

属性和字段不是一对一的。属性是关于类的接口(无论是公共接口还是内部接口),而字段是关于类的实现。属性不应被视为仅公开字段的一种方式,它们应被视为公开类的意图和目的的一种方式。

就像您使用属性向您的消费者提供关于什么构成您的类的合同一样,您也可以出于非常类似的原因向自己提供合同。所以是的,我确实在有意义的时候使用私有属性。有时私有属性可以隐藏实现细节,例如延迟加载,属性实际上是多个字段和方面的集合,或者属性需要在每次调用时虚拟实例化(想想DateTime.Now)。有时候,即使在课程的后端对自己强制执行此操作也是有意义的。

于 2010-07-22T15:01:45.283 回答
9

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

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}
于 2010-07-22T15:29:10.000 回答
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;
  }
}
于 2010-07-22T15:37:18.167 回答
6

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

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

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

于 2014-12-03T10:25:23.057 回答
5

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

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

于 2010-07-22T14:55:31.420 回答
3

我知道这个问题已经很老了,但以下信息不在当前的任何答案中。

我无法想象我什么时候需要能够在内部获取但不设置

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

此外,Visual Studio Professional 将提供有关属性而不是字段的信息,以便更轻松地查看您的字段正在使用的内容。

场域

于 2019-04-03T12:14:54.777 回答
2

一种常见的做法是仅使用 get/set 方法修改成员,甚至是私有方法。现在,这背后的逻辑是让您知道您的 get/set 始终以特定方式运行(例如,触发事件),这似乎没有意义,因为这些不会包含在属性方案中......但旧习难改。

于 2010-07-22T14:57:17.500 回答
2

当存在与属性集或获取相关的逻辑(想想延迟初始化)并且该属性在类中的一些地方使用时,这是非常有意义的。

如果它只是一个直接的支持领域?没有什么是好的理由。

于 2010-07-22T14:58:15.080 回答
2

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

  • 验证

    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

于 2018-08-25T01:59:52.797 回答
1

另一种用法是在设置值时进行一些额外的操作。

在我的情况下,它发生在 WPF 中,当我显示一些基于私​​有对象(未实现INotifyPropertyChanged)的信息时:

private MyAggregateClass _mac;

private MyAggregateClass Mac
{
    get => _mac;
    set
    {
        if(value == _mac) return;
        _mac = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayInfo)));
    }
}

public string DisplayInfo => _mac.SomeStringInformationToDisplayOnUI;
        

也可以有一些私有方法,例如

private void SetMac(MyAggregateClass newValue)

要做到这一点。

于 2020-11-12T16:47:54.667 回答
0

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

  • 你需要使用reforout值 - 也许是因为它是一个Interlocked计数器
  • 旨在表示基本布局,例如在struct具有显式布局的情况下(可能映射到 C++ 转储或unsafe代码)
  • 从历史上看,该类型已BinaryFormatter与自动字段处理一起使用(更改为 auto-props 会更改名称,从而破坏序列化程序)
于 2019-05-23T14:05:54.947 回答
0

各种答案都提到了使用属性来实现惰性成员。这个答案讨论了使用属性制作实时别名。我只是想指出,这两个概念有时会同时出现。

当使用属性为另一个对象的公共属性创建别名时,该属性的惰性被保留:

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private IDbConnection Conn => foo.bar.LazyDbConnection;

另一方面,在构造函数中检索该属性将否定惰性方面:

Conn = foo.bar.LazyDbConnection;
于 2021-07-15T18:45:32.977 回答
0

查看指南(Properties (C# Programming Guide)),似乎没有人希望将属性用作私有成员。

属性使类能够公开获取和设置值的公共方式,同时隐藏实现或验证代码。

在任何情况下,它都可以通过一种或两种方法互换,反之亦然。

因此,原因可能是在get和 get 字段语法上保留括号。

于 2020-11-26T15:01:51.823 回答