在自动属性中访问支持字段

Jon*_*ker 8 c# encapsulation properties .net-3.5 c#-3.0

有没有办法访问属性的支持字段,以便进行验证,更改跟踪等?

是否有类似以下内容?如果没有,是否有计划在.NET 4/C#4中使用它?

public string Name
{
    get;
    set
    {
        if (value != <Keyword>)
        {
            RaiseEvent();
        }
        <Keyword> = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

我遇到的主要问题是使用自动属性不允许在具有明确支持字段的属性的验证等方面具有相同的灵活性.然而,显式支持字段在某些情况下具有缺点,即允许其所包含的类在访问和重用属性的验证,更改跟踪等时访问支持字段,就像可能正在访问的任何其他类一样外部的财产.

在上面的示例中,对支持字段的访问将限定为属性,从而防止绕过属性验证,更改跟踪等.

编辑:我已将<Backing Field>更改为<Keyword>.我会提出一个类似于价值的新关键字.虽然我确信它在很多现有代码中被使用,但是字段会很好.

Ray*_*Ray 13

不,没有.如果要访问支持字段,请不要使用自动属性并自行滚动.

我同意,拥有一个只能由财产而不是其他同类人员访问的字段会很棒.我会一直使用它.

  • 课程必须能够相信自己能够妥善处理自己的私人. (3认同)
  • IMO这是*实际*问题的答案.标记为答案的答案是对一个单独的切线问题的答案:/ (2认同)

Dan*_*ull 6

正如MSDN所述:

"在C#3.0及更高版本中,当属性访问器中不需要额外的逻辑时,自动实现的属性使属性声明更简洁.它们还使客户端代码能够创建对象当您声明属性时,如以下示例所示,编译器创建一个私有的,匿名的后备字段只能通过属性的get和set访问器来访问."

由于在访问器中有其他逻辑,因此在您的方案中使用自动实现的属性是不合适的.

虽然支持字段确实存在,但它会被赋予一个错误的名称以阻止您轻松引用它 - 这个想法是您永远不会直接引用该字段.为了利益起见,您可以使用Reflector来反汇编代码并发现字段名称,但我建议您不要直接使用该字段,因为此名称可能确实存在易变性,因此您的代码可能随时中断.


Dan*_*ull 3

阅读了您在梅尔达德回答中的评论后,我想我更好地理解了您的问题。

您似乎担心开发人员访问他们正在编写的类中的私有状态、绕过验证逻辑等的能力。这表明状态根本不应该包含在类中。

我建议采取以下策略。编写一个表示 ValidatedValue 的通用类。此类仅保存支持值,并且仅允许通过 get 和 set 方法进行访问/更改。委托被传递给 ValidatedValue 来表示验证逻辑:

public class ValidatedValue< T >
{
    private T m_val;
    public ValidationFn m_validationFn;

    public delegate bool ValidationFn( T fn );

    public ValidatedValue( ValidationFn validationFn )
    {
        m_validationFn = validationFn;
    }

    public T get()
    {
        return m_val;
    }

    public void set(T v)
    {
        if (m_validationFn(v))
        {
            m_val = v;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以根据需要添加更多代表(例如,支持更改前/更改后通知)。

您的类现在将使用 ValidatedValue 代替您的属性的后备存储。

下面的示例显示了一个类 MyClass,其整数经验证小于 100。请注意,引发异常的逻辑位于 MyClass 中,而不是 ValidatedValue 中。这允许您执行依赖于 MyClass 中包含的其他状态的复杂验证规则。Lambda 表示法用于构造验证委托 - 您可以改为绑定到成员函数。

public partial class MyClass
{
    private ValidatedValue<int> m_foo;

    public MyClass()
    {
        m_foo = new ValidatedValue<int>(
            v => 
            {
                if (v >= 100) RaiseError();
                return true;
            }
        );
    }

    private void RaiseError()
    {
        // Put your logic here....
        throw new NotImplementedException();
    }

    public int Foo
    {
        get { return m_foo.get(); }
        set { m_foo.set(value); }
    }
}
Run Code Online (Sandbox Code Playgroud)

希望有所帮助 - 有点偏离原来的主题,但我认为这更符合您的实际担忧。我们所做的是将验证逻辑从属性中取出并放在数据上,这正是您想要的地方。

  • 您可以通过在 ValidatedValue 类上向 T 添加运算符= 和强制转换来使语法变得更好。基本上你希望它的行为就像 ValidatedValue&lt;T&gt; 是一个 T。 (2认同)