使用.NET属性的最佳实践是什么?

5

我对属性应该做多少有些困惑,我听说属性应该始终表示类的一个逻辑属性。 除了ArgumentOutOfRange之外,Get和Set几乎不应该抛出异常。这是真的吗?以下示例完全错误吗?

public bool DeviceRegistered
{
    get{ return _Registered;}
    set
    {
        if(value)
        {
            RegisterDevice();
            _Registered = true;
        }
        else
        {
            UnRegisterDevice();
            _Registered = false;
        }
    }
}

同样,如果在同一类中的一个方法想要更改属性的值,应该通过属性的set访问器还是直接修改私有变量_Registered呢?

如果您在使用属性时有任何其他建议,请包括在内!谢谢。

7个回答

6
这里有来自 MSDN 的 属性设计指南。尤其要注意属性与方法的区别部分。
根据我个人的经验,你不应该使用属性来完成大量的工作。它们应该返回已经检索到的信息。我目前正在处理一个代码库,其中有很多懒加载的属性从 Web 服务中检索信息。在调试时查看类的属性会导致所有属性被评估,导致功能评估超时并使 ASP.NET 进程崩溃。

你会建议对于懒加载属性应该采取什么做法? - Greg
我们已经决定使用服务来填充属性。大多数情况下,我们发现对象每个请求只使用一次。如果它被使用超过一次,服务将其存储在缓存中(例如 Http.Items)。这个缓存检索逻辑也可以抽象出来,并根据需要在服务之间共享。 - Richard Nienaber
3
将私有后备字段改为System.Lazy<T>类型,从属性中返回 _lazyValue.Value,并将该属性标记为 [DebuggerBrowsable(DebuggerBrowsableState.Never)] - Sam Harwell
@280Z28: 仅适用于.NET 4.0:http://msdn.microsoft.com/zh-cn/library/dd642331(VS.100).aspx - John Saunders
@John Saunders:你可以在MEF源代码分发(链接1)中获取它,使用MS-PL许可证,或者你可以使用来自Reactive Extensions for .NET(链接2)的System.Threading.dll中更好的版本。 1)http://mef.codeplex.com/ 2)http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx - Sam Harwell
@280Z28:谢谢你。这个资格证书是必要的,因为它不在.NET 3.5中。 - John Saunders

4
在这种情况下,我认为使用方法更加合理,因为你正在执行一个操作。
private volatile bool _DeviceRegistered;
private readonly object _Lock = new object();

public bool DeviceRegistered
{
    get { return _DeviceRegistered; }
}

public void RegisterDevice()
{
    lock (_Lock) // A good idea to lock
    {
        // ........
        _DeviceRegistered = true;
    }
}

public void UnregisterDevice()
{
    lock (_Lock)
    {
        // ........
        _DeviceRegistered = false;
    }
}

1
您可能还希望锁定get属性访问器,以便在读取属性时不会出现“……”正在进行中(可能在单独的线程上)。 - Jesse C. Slicer
@Jesse - 问题是我们是否想在注册设备时占用其他线程?我认为为了安全起见,你是对的,但这实际上取决于我们尚未看到的代码。 - ChaosPandion

1
一个简短的回答:当我有一个需要花点工夫才能获取的值,但是我希望“整个世界”(包括同一类中的调用者)将其视为变量且始终可用时,我喜欢使用只读属性。这个属性会执行获取值的工作,然后简单地返回(可能会进行缓存等优化)。
另一种选择是使用“get”方法,也可以,但是当我不希望调用者认为获取/计算值是需要工作量时,我喜欢使用属性。

0

我承认,通常情况下,让属性不仅仅保存值是有意义的,但在我看来,你的例子很糟糕。我会创建一个注册/注销设备的方法和一个简单的获取当前状态的getter。我的一般规则是,如果我正在执行一个操作,我使用方法,但如果我只是改变一个值,那么属性更合适。现在,属性如何处理可能涉及一些计算或I/O,但重要的是类消费者的期望。

public void Register()
{
  ...do some stuff...
  this.Registered = true;
}

public void Unregister()
{
  ...do some stuff...
  this.Registered = false;
}

public bool Registered { get; private set; }

此外,我倾向于强制让类代码通过属性进行操作——这将使我对属性操作方式所做的任何更改都隔离到属性代码本身。同一类的其他部分不需要知道属性的工作原理。当然,也会有例外情况——比如当您想要或需要避免属性执行某些计算,但仍需要更新值时——但这是我的一般规则。

0
在这种情况下,由于在属性更改时调用另一个方法,如果您想保留该功能,则应使用Accessor进行设置。如果只是存储值,则直接使用_Registered变量会稍微好一些。

0
为了解决直接使用成员变量或使用属性访问器/修改器的问题,我更支持使用属性。如果你在访问器中返回默认值,或者在修改器中触发属性更改事件(或类似事件),通过直接访问成员变量来绕过此功能。如果扩展类重写属性,如果直接访问成员变量,则可能无意中绕过扩展类。
有些情况下,访问成员变量(私有)是正确的方法,但我总是倾向于使用属性,除非有充分的理由访问成员变量。

0

这里是我随着时间推移所意识到的一些规则。大部分是我的个人观点,但我认为它们是好主意。 :) 编辑:我刚刚意识到这些规则大多已经在属性使用指南中涵盖了。

获取器

  • 你应该优先选择不改变状态的getter。如果你有一个getter会改变程序状态,请使用[DebuggerBrowsable(DebuggerBrowsableState.Never)]标记它。
  • 优先选择可以从任何线程调用的getter。
  • 优先选择计算简单的getter,因为它们被使用的方式让人们认为它们不会产生性能损失。如果它们需要一些时间来执行,请使用上面的DebuggerBrowsable属性或[DebuggerDisplay("Some display text")](其中文本是简单计算的)。忘记后者可能会对调试器性能产生不利影响。
  • Getter可能会抛出以下异常:
    • InvalidOperationException
    • ObjectDisposedException

Setters

  • 建议每当您设置两个属性时,无论哪个先来都没有关系。
  • 如果setter有一个不能通过公开暴露的属性进行测试的前提条件,则应将其标记为protected或private。
  • 限制调用setter只针对特定线程是可以的,但如果这样做,需要进行文档记录,并且您的对象应该实现ISynchronizeInvoke或公开Dispatcher属性。
  • Setters可以抛出至少以下异常:
    • ArgumentException(适当时为ArgumentNullExceptionArgumentOutOfRangeException等)
    • InvalidOperationException
    • ObjectDisposedException

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接