在c#中使用私有属性有什么原因吗?

我刚刚意识到c# 财产构造也可以与私人访问修饰符一起使用:

private string Password { get; set; }

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

private string _password;

并且我无法想象什么时候我需要能够在内部get but not setset but not get作为私有字段:

private string Password { get; }

private string Password { set; }

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

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

齿顶高

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

  • 当需要惰性加载私有字段时
  • 当私有字段需要额外的逻辑或者是计算值时
  • 因为私有字段很难调试
  • 为了“向自己展示一份合同”
  • 将公开的属性作为序列化的一部分在内部转换/简化
  • 包装要在类中使用的全局变量
94913 次浏览

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

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

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

即使当我不需要属性的getter或setter上的逻辑时,我也会使用这种方法。使用属性(即使是私有属性)确实有助于您的代码不受未来的影响,以便您可以在需要时将逻辑添加到getter。

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


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

public string Password
{
get;
private set;
}

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

通常只修改使用get/set方法的成员,甚至是私有方法。现在,这背后的逻辑是,所以你知道你的get/set总是以一种特定的方式表现(例如,触发事件),这似乎没有意义,因为这些不会包括在属性方案中……但旧习难改。

当存在与属性set或get相关的逻辑时(考虑惰性初始化),并且属性在类中的几个地方使用时,这是非常有意义的。

如果它只是一个直接的后场?我想不出什么好理由。

这是我唯一能想到的用法

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

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

就像使用属性向消费者展示构成类的契约一样,出于非常相似的原因,您也可以向自己展示契约。所以,当有意义的时候,我确实会使用私有财产。有时私有属性可以隐藏实现细节,比如延迟加载,属性实际上是几个字段和方面的集合,或者属性需要在每次调用时虚拟实例化(想想DateTime.Now)。有些时候,即使在类的后端对自己实施这一点也是有意义的。

惰性初始化是它们可以很整洁的一个地方,例如。

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

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

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


return _password;
}
}

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

在我的代码中,它的主要用途是惰性初始化,就像其他人提到的那样。

在字段上使用私有属性的另一个原因是私有属性比私有字段更容易调试。我经常想知道一些事情,比如“这个领域正在出人意料地设置;谁是第一个设置这个字段的调用者?”如果你能在setter上放一个断点,然后点击go,那就简单多了。你可以把登录放到这里。你可以把绩效指标放进去。您可以放入在调试版本中运行的一致性检查。

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

我在序列化中使用它们,像DataContractSerializer或protobuf-net这样的东西支持这种用法(XmlSerializer不支持)。如果你需要简化一个对象作为序列化的一部分,它是有用的:

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

我一直在做的一件事是将“全局”变量/缓存存储到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;
}
}

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

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

如果有很多子属性,它是有用的。

好吧,就像没人提到的,你可以用它来验证数据或锁定变量

  • < p > 验证

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

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

    注意:当锁定引用时,并不会锁定对被引用对象成员的访问。 < / p >

惰性引用:当惰性加载时,你可能最终需要异步执行,现在有AsyncLazy。如果你使用的是比Visual Studio SDK 2015更老的版本,或者没有使用它,你也可以使用AsyncEx的AsyncLazy

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

我无法想象什么时候我需要内部获取而不是设置

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

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

PorpField

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

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

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

在我的例子中,它发生在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)

这样做。

查看准则(属性(c#编程指南)),似乎没有人期望将属性用作私有成员。

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

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

因此,原因可能是在得到上留出括号,并在设置上获得字段语法。

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

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

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

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

Conn = foo.bar.LazyDbConnection;