正确编码直接访问 属性 C# 的支持字段

Proper Coding Direct Access to the backing field of a Property C#

我看过一些代码并认为它​​似乎有问题,所以我想知道它是否可以接受良好的编码,我的第一个想法是否定的。

考虑:

class MyClass
{
    private string m_MySuperString;
    public string MySuperString
    {
        get { return m_MySuperString; }
        set { m_MySuperString = value; }
    }

    public void MyMethod()
    {
        if (blah != yada)
        {
             m_MySuperString = badabing;
        }
    }

    public void MyOtherMethod()
    {
        if (blah == yada)
        {
            m_MySuperString = badaboom;
        }
    }
}

这种对 Backing Field 的直接访问是否是一种可接受的做法,或者它是否是糟糕的编码 - 或者我应该问一下 属性 访问器的意义是什么,如果这是在 class 与 public 成员,多个组件允许访问 - 是否可能发生崩溃 - 我会冒险在多线程应用程序中发生崩溃。

有什么想法吗? 我在 SO 和其他人上看过这个 Link> Why use private members then use public properties to set them?

编辑

让我说清楚,因为提供了很好的信息,而是直接回复所有答案和评论。 我不是在问属性是什么,不是问我是否可以自动实现属性、私有 setters、OnValueChange 通知、属性逻辑。 我的问题是关于直接访问该支持字段 - 例如,如果你说一个多线程场景 - getters/setters 上的同步时钟的全部意义不是控制对支持字段的访问吗?这种代码在那种情况下是否可以接受 - 只需向 getter 和 setter 添加一个 syncLock ??请记住 myClass 的构造函数中的代码是一个示例 - 代码可以在任何其他方法中 - 例如更新的 class - Method1

编辑结束

在描述的用例中,您可以使用自动实现的属性按如下方式定义

public string MySuperString{ get;  set ;}

如果您需要进行一些输入验证或者 属性 与内部字段不同,例如

,您应该使用支持字段

public string FullName{ get { return firstName + LastName} }

使用属性的另一个好处是您可以在界面中定义它们,这在较长的时间里更好 运行 以便将来添加功能

面向对象编程 (OOP) 中的属性有助于实施 Encapsulation。这个想法是只允许对象本身与其自己的数据(即字段)进行交互。只能通过方法从外部访问对象的数据。例如,在 Java 中,您必须显式编写 get 和 set 方法。 C# 中的属性有一种特殊的语法,将这两种方法结合在一个结构中,但 getters 和 setters 实际上是方法。

这也意味着绝对允许一个对象直接访问它自己的字段。

然而,在某些情况下,属性 getters 和 setters 执行额外的逻辑。 setter 可能引发 PropertyChanged 事件或进行一些验证。 getter 可能会组合多个字段或生成格式化或计算值。如果您需要执行此附加逻辑,则必须访问属性而不是字段。如果 属性 是自动实现的,那么您别无选择(在 C# 中),因为支持字段是隐藏的且不可访问。 (In VB it is hidden from IntelliSense but accessible from within the class.)

我建议查看@JonSkeet 的《深入了解 C#》的第 8 章第 1 节(出于教育目的,我无耻地从中摘录了以下片段)以获取有关自动实现的属性的更多信息。简单回答你的问题,不,这段代码没有任何问题。

考虑以下片段:

public string Name { get; set; }

编译为

private string <Name>k__BackingField;
public string Name
{
     get { return <Name>k__BackingField; }
     set { <Name>k__BackingField = value; }
}

...因此编译器已经在为您完成您在上面完成的工作。有一些方法可以修改它正在做的事情,但这些方法并不能真正回答问题。线程安全书中给出的一个例子是这样的:

//code above, plus
private static int InstanceCounter { get; set; }
private static readonly object counterLock = new object();

public InstanceCountingPerson(string name, int age) {
    Name = name;
    Age = age;
    lock (counterLock) // safe property access
    {
        InstanceCounter++;
        // and whatever else you have to do with the lock enabled
    }
}

--这是一个也被引用的模式 in this SO question。然而,正如那里所指出的,锁 (a) 可能很慢,(b) 可能无法真正确保他们的工作完成,因为他们必须在某个时候被释放,以及 (c) 依赖于信任系统,因为他们有点天真地假设任何想要访问该对象的东西都会正确使用锁(并不总是正确的,至少在我见过的一些代码中不是这样 :D )。 getter 和 setter 方法的优点是您可以为 class 的任何实例强制使用锁的模式(阅读:正确封装字段,正如其他人所建议的那样) .

但是,您可能会考虑的另一种模式是控制反转。使用依赖注入容器,您可以指定您喜欢的线程安全级别。如果你觉得每个人都在等待你的对象的单例实例,你可以声明所有对该对象接口的引用都引用同一个对象(并且必须等待该对象可用),或者你可以确定一个线程安全的每次请求时都应创建对象的实例。有关详细信息,请参阅

注:

任何经过同行评审的对上述想法的批评都将被慷慨地接受并添加到答案中,因为此时我是一个线程安全爱好者。