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

时间:2016-06-20 20:45:41

标签: c# properties coding-style standards

我已经看过一些代码,并认为它有些不对劲,所以我想知道它是否可以接受良好的编码,我的第一个想法是否定的。

考虑:

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是一种可接受的做法,还是编码错误 - 或者我应该问一下Property Accessor的重点是什么?如果这是在公共成员的类内部完成的,那么访问权限是多个组件允许 - 是否可能发生崩溃 - 我会冒险在多线程应用程序中发生崩溃。

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

修改

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

结束编辑

3 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

在所描述的用例中,您可以使用自动实现的属性

将其定义如下
public string MySuperString{ get;  set ;}

如果您需要进行一些输入验证,或者该属性与内部字段不同,您应该使用后备文件,例如

  

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

使用属性的另一个好处是您可以在界面中定义它们,从长远来看,这样可以更好地添加未来的功能

答案 2 :(得分:1)

我建议查看@ JonSkeet的C#In Depth的第8章第8节(我已经无耻地将下面的片段用于教育目的),以获取有关自动实现的属性的更多信息。简而言之,回答你的问题,不,这段代码没有错。

请考虑以下代码段:

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
    }
}

...所以编译器已经为你完成了上面所做的工作。有一些方法可以修改它正在做的事情,但那些并没有真正回答这个问题。本书中提到的线程安全性的一个例子是:

{{1}}

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

然而,您可能会考虑的另一种模式是控制反转。使用Dependency Injection容器,您可以指定您熟悉的线程安全级别。如果您对等待对象的单例实例的所有人感到满意,则可以声明对对象接口的所有引用都引用相同的对象(并且必须等待对象变为可用),或者您可以确定线程安全每次请求时都应创建对象的实例。有关详细信息,请参阅this SO answer

注意:

任何经过同行评议的对上述观点的批评都将被慷慨地接受并添加到答案中,因为我现在在某种程度上是一个线索安全的副作品。