【问题标题】:Proper Coding Direct Access to the backing field of a Property C#正确编码直接访问属性 C# 的支持字段
【发布时间】:2016-06-20 20:45:41
【问题描述】:

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

考虑:

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

这种对支持字段的直接访问是可接受的做法还是编码不好-或者我应该问一下属性访问器的意义是什么,如果这是在具有公共成员的类内部完成的,访问是多个组件允许 - 是否可能发生崩溃 - 我会冒险在多线程应用程序中发生崩溃。

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

编辑

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

结束编辑

【问题讨论】:

  • 这真的很主观。我建议查看@JonSkeet 的 C# In Depth 的第 8 章第 1 节,以获取有关自动实现属性的更多信息。简短回答您的问题,不,这段代码没有任何问题。
  • 您可以在课堂上做任何您认为需要的事情。该属性用于您与其他类型的接口。如果有帮助,您可以阅读 OO 封装原则。
  • @Chaim Eliyah 好的,所以代码没有问题;关于这个和来自类外部的多线程访问是否有任何问题 - 如果支持字段在内部设置(甚至可能来自类中的两个方法)并且可以在外部完成对属性/方法的访问如何控制访问到支持字段,除了所有访问都通过属性?我认为在这个类上运行多个线程会导致问题 - 还是我完全错了?我认为这就是在属性访问器上放置同步锁的全部意义?
  • 您可以预料的是,在多线程应用程序中,blahyada 的值可能会在执行代码之前发生变化,因此您可能会遇到不可预知的行为。你可以使用locks 或者你可以使用thread-safe container 或者你可以决定你不在乎,因为毕竟这些是托管运行时语言中的内存属性并且相信执行会发生按照预测的顺序。这真的取决于你的设计。
  • @Chaim Eliyah,好的,如果您将 cmets 作为答案发布 - 我会接受。也感谢您提供的链接 - 我会在业余时间阅读更多内容。

标签: c# properties coding-style standards


【解决方案1】:

面向对象编程 (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.)

【讨论】:

  • "这也意味着绝对允许一个对象直接访问它自己的字段。"仅仅因为我可以就意味着我应该吗?附加逻辑更符合我的观点,例如多线程应用程序中的同步锁,属性访问器上的同步锁不是控制对支持字段的访问吗?当内部访问直接在支持字段上时线程会崩溃,比如我有一个外部组件请求访问。
  • 封装原则不要求对象通过方法调用访问自己的数据。因此,我认为这样做并不是一个坏习惯。但当然,如果逻辑需要,那是另一回事。同步逻辑在这方面没有任何不同。
【解决方案2】:

我建议查看@JonSkeet 的 C# In Depth 的第 8 章第 1 节(出于教育目的,我无耻地从中获取了以下 sn-ps)以获取有关自动实现属性的更多信息。简短回答您的问题,不,这段代码没有任何问题。

考虑以下sn-p:

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 方法的优点是您可以对类的任何实例强制使用锁的模式(阅读:正确封装字段,正如其他人建议的那样)。

不过,您可能会考虑的另一种模式是控制反转。使用依赖注入容器,您可以指定您熟悉的线程安全级别。如果您对每个人都在等待对象的单例实例感到满意,则可以声明对对象接口的所有引用都引用同一个对象(并且必须等待对象变为可用),或者您可以确定线程安全每次请求时都应创建对象的实例。详情请见this SO answer

注意:

任何经过同行评审的对上述想法的批评都将被欣然接受并添加到答案中,因为我在这一点上有点像线程安全外行。

【讨论】:

    【解决方案3】:

    在所描述的用例中,您可以使用自动实现的属性将其定义如下

    public string MySuperString{ get;  set ;}
    

    如果您需要进行一些输入验证或属性与内部字段不同,则应使用支持文件

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

    使用属性的另一个好处是您可以在接口中定义它们,从长远来看,这对于将来添加的功能更好

    【讨论】:

    • 另外请注意,如果您的要求如此,例如,您可以为 getset 使用不同的访问修饰符,例如你可以做public string MyProp { get; private set; }
    • 虽然这个问题很主观,但我赞成这个答案,因为支持字段是一个实现细节,在这种情况下可以抽象出来。更高的抽象性 = 意外错误的可能性更小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-03
    • 2013-05-06
    • 2017-04-26
    • 2012-02-07
    • 2014-05-23
    相关资源
    最近更新 更多