有没有理由在C#中使用私有属性?

时间:2010-07-22 14:52:15

标签: c# properties access-modifiers

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

private string Password { get; set; }

虽然这在技术上很有意思,但我无法想象何时使用它,因为私人领域甚至涉及较少的仪式

private string _password;

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

private string Password { get; }

private string Password { set; }

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

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

附录

很好的答案,阅读它们我剔除了私人财产的这些用途:

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

19 个答案:

答案 0 :(得分:186)

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

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

        return _password;
    }
}

答案 1 :(得分:121)

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

私有属性优于字段的另一个原因是私有属性比私有字段更容易调试。我经常想知道这样的事情:“这个字段是意外设置的;谁是设置这个字段的第一个调用者?”如果你可以在setter上放一个断点然后点击它就会更容易。你可以把登录放在那里。您可以将性能指标放在那里。您可以放入在调试版本中运行的一致性检查。

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

答案 2 :(得分:39)

  

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

即使我不需要在属性的getter或setter上使用逻辑,我个人也会使用它。使用属性,甚至是私有属性,确实可以帮助您在未来验证代码,以便以后可以根据需要将逻辑添加到getter中。

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


在一个半相关的案例中(虽然与你的问题不同),我经常在公共财产上使用私人制定者:

public string Password 
{
    get; 
    private set;
}

这为您提供了一个公共的getter,但保持了setter的私密性。

答案 3 :(得分:19)

延迟初始化是一个可以整洁的地方,例如。

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

答案 4 :(得分:17)

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

答案 5 :(得分:12)

我能想到的唯一一种用法

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

答案 6 :(得分:8)

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

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

答案 7 :(得分:7)

我在序列化中使用它们,使用支持此用法的DataContractSerializer或protobuf-net(XmlSerializer不支持)。如果您需要将对象简化为序列化的一部分,那么它非常有用:

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

答案 8 :(得分: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;
  }
}

答案 9 :(得分:5)

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

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

答案 10 :(得分:5)

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

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

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

答案 11 :(得分:2)

当存在与属性集或get相关的逻辑(想想延迟初始化)并且该属性在类中的一些地方使用时,这是完全合理的。

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

答案 12 :(得分:2)

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

答案 13 :(得分:1)

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

  • 验证

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div>foo</div>
    <div>foo_bar</div>
    <div>bar</div>
  • 锁定

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

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

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

答案 14 :(得分:0)

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

  

我无法想象何时需要内部获取但未设置

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

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

PorpField

答案 15 :(得分:0)

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

  • 您需要使用带有值的refout-可能是因为它是一个Interlocked计数器
  • 打算表示基本布局,例如在具有明确布局的struct上表示(可能映射到C ++转储或unsafe代码)
  • 从历史上看,该类型已与BinaryFormatter一起使用,并具有自动字段处理功能(更改为auto-props会更改名称,从而破坏序列化程序)

答案 16 :(得分:0)

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

在我的情况下,这会在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)

做到这一点。

答案 17 :(得分:0)

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

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

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

所以原因可能是在 getting 上保留了括号,而在 setting 上获得了字段语法。

答案 18 :(得分:0)

各种答案都提到使用属性来实现惰性成员。并且 this answer 讨论了使用属性来创建实时别名。我只是想指出这两个概念有时会一起出现。

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

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

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

Conn = foo.bar.LazyDbConnection;