8

有什么方法可以访问属性的支持字段以进行验证、更改跟踪等?

可能会出现以下情况吗?如果没有,是否有计划在 .NET 4 / C# 4 中使用它?

public string Name
{
    get;
    set
    {
        if (value != <Keyword>)
        {
            RaiseEvent();
        }
        <Keyword> = value;
    }
}

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

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

编辑:我已将 <Backing Field> 更改为 <Keyword>。我会提出一个类似于 value 的新关键字。字段会做得很好,尽管我确信它已在很多现有代码中使用。

4

6 回答 6

13

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

我同意拥有一个只能由物业访问而不能由班级其他人访问的字段会很棒。我会一直使用它。

于 2009-03-15T22:42:10.477 回答
6

正如MSDN所述:

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

由于您的访问器中有额外的逻辑,因此在您的场景中使用自动实现的属性是不合适的。

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

于 2009-03-15T23:06:13.133 回答
3

在阅读了您在 Mehrdad 的回答中的评论后,我想我对您的问题有了更好的理解。

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

我建议以下策略。编写一个表示 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;
        }
    }
}

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

您的班级现在将使用 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); }
    }
}

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

于 2009-03-16T00:49:37.270 回答
2

不,但您可以在子类中:

public class Base
{
    public string Name
    {
        get;
        virtual set;
    }
}

public class Subclass : Base
{
    // FIXME Unsure as to the exact syntax.
    public string Name
    {
        override set
        {
            if (value != base.Name)
            {
                RaiseEvent();
            }

            base.Name = value;
        }
    }
}
于 2009-03-16T01:35:36.787 回答
1

如果您要这样做,为什么要使用自动属性?!

一个简单的属性早在 1.0 中就已经完成了。我认为为每种特殊情况增加语言的复杂性是没有意义的。您要么需要该属性来执行普通的存储/检索模型,要么需要更多。在后一种情况下,正常的属性就可以了。

于 2009-03-15T22:40:26.537 回答
1

恐怕你不能这样做。这就是我开始编写MoXAML Power Toys的原因之一,以提供将自动属性转换为 Notify 属性的能力。

于 2009-03-15T22:44:56.807 回答