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

Are there any reasons to use private properties in C#?

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

1
private string Password { get; set; }

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

1
private string _password;

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

1
private string Password { get; }

1
private string Password { set; }

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

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

补遗

很好的答案,通过阅读我挑选了这些私人财产的用途:

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


如果我需要缓存一个值并希望懒惰地加载它,我就使用它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}


在我的代码中,这一点的主要用法是惰性初始化,正如其他人所提到的。

字段上私有属性的另一个原因是私有属性比私有字段更容易调试。我经常想知道"这个字段是意外设置的;谁是设置这个字段的第一个调用者?"如果你能在二传手上放一个断点,然后点击Go,那就简单多了。你可以把日志放在那里。您可以将性能指标放在其中。您可以放入在调试版本中运行的一致性检查。

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


perhaps there is a use case with nested / inherited classes or perhaps where a get/set might contain logic instead of just giving back the value of the property

即使我不需要在属性的getter或setter上使用逻辑,我个人也会使用它。使用一个属性,甚至是一个私有的属性,确实有助于将来验证代码,以便在以后需要时向getter添加逻辑。

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

在一个半相关的案例中(尽管与您的问题不同),我经常在公共财产上使用私有设置器:

1
2
3
4
5
public string Password
{
    get;
    private set;
}

这会给你一个公开的getter,但会让setter保密。


惰性初始化是一个可以保持整洁的地方,例如

1
2
3
4
5
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 get only属性的一个好用法是计算值。有几次,我有一些属性是私有的只读的,只需要对我类型中的其他字段进行计算。它不值得一个方法,对其他类也不感兴趣,所以它是私有属性。


我唯一能想到的用法

1
2
3
4
5
6
7
private bool IsPasswordSet
{
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}


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

就像你用财产向你的消费者展示一份关于什么是你的类的合同一样,你也可以以非常相似的理由向你自己展示一份合同。所以是的,我确实在有意义的时候使用私有财产。有时,私有属性可以隐藏实现细节,例如延迟加载、属性实际上是多个字段和方面的聚集,或者属性需要在每次调用时进行虚拟实例化(想想DateTime.Now)。有时甚至在类的后端对自己强制执行这一点也是有意义的。


我在序列化中使用它们,比如支持这种用法的DataContractSerializer或protobuf net(XmlSerializer没有)。如果需要将对象简化为序列化的一部分,则此功能非常有用:

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

我一直在做的一件事是将"全局"变量/缓存存储到HttpContext.Current中。

1
2
3
4
5
6
7
8
9
10
11
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;
  }
}

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

1
2
3
4
    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

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


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

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


当存在与属性集或get关联的逻辑(想想懒惰的初始化)并且该属性在类中的一些地方被使用时,这是完全有意义的。

如果只是一个直接的后场?没有什么能成为一个好理由。


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


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

  • 验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }

            _password = value;
        }
    }
  • 锁定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    object _lock = new object();
    object _lockedReference;
    object LockedReference
    {
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

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

懒惰引用:当懒惰加载时,您可能最终需要进行异步操作,而现在有了异步懒惰。如果您使用的是比Visual Studio SDK 2015更旧的版本或不使用它,您也可以使用AsyncEx的AsyncLazy。


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

  • 您需要使用带值的refout-可能是因为它是Interlocked计数器
  • 它的目的是表示基本布局,例如,在EDCOX1,6,具有显式布局(可能映射到C++转储,或EDCOX1×7代码)。
  • 历史上,该类型与BinaryFormatter一起使用,具有自动字段处理功能(更改为auto props会更改名称,从而中断序列化程序)。

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

I can't imagine when I would ever need to be able to internally get but not set

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

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

PorpField