除了通知状态改变外,C#中的属性是否还会有副作用?
我已经看到以几种不同方式使用属性。从将在首次访问值时加载值的属性到具有大量副作用(例如导致重定向到其他页面)的属性。
除了通知状态改变外,C#中的属性是否还会有副作用?
我已经看到以几种不同方式使用属性。从将在首次访问值时加载值的属性到具有大量副作用(例如导致重定向到其他页面)的属性。
Answers:
我假设您是在谈论只读属性,或者至少是属性getters,因为在几乎所有情况下,属性设置器都会产生副作用。否则,它不是很有用。
通常,一个好的设计遵循最小的惊喜原则。不要做呼叫者不希望您做的事情,尤其是当这些事情可能改变未来的结果时。
通常,这意味着属性获取器不应具有副作用。
但是,请注意“副作用”的含义。
从技术上讲,副作用是对状态的任何修改。那可能是可公开访问的状态,或者……可能是完全私有的状态。
延迟/延迟加载程序是状态的一种示例,几乎完全是私有的。只要释放资源不是调用者的责任,那么实际上您通常通过使用延迟初始化来减少意外和复杂性。调用者通常不期望必须显式表示内部结构的初始化。因此,延迟初始化并没有违反上述原则。
另一个示例是同步属性。为了使方法具有线程安全性,通常必须使用临界区或互斥体对其进行保护。进入关键部分或获取互斥体是副作用。您正在修改状态,通常是全局状态。但是,此副作用是必需的,以防止发生更糟糕的意外情况-数据被另一个线程修改(或更糟的是部分修改)的意外情况。
因此,我将对以下内容的限制放宽一些:属性读取不应具有可见的副作用或改变其语义的副作用。
如果呼叫者不可能受到副作用的影响,甚至意识不到副作用,那么该副作用就不会造成任何伤害。如果您不可能编写测试来验证特定副作用的存在,那么它已经足够本地化以标记为私有实现细节,因此对外界没有任何合法关注。
但是要小心;根据经验,应该尝试避免产生副作用,因为通常您认为是私有实现细节的内容可能会意外泄漏并公开。
我将用一个问题回答您的问题:修改表单的Width属性时会发生什么?
就在我的头顶上,这将:
(免责声明:我是Delphi开发人员,而不是.NET开发人员。对于C#来说,这可能不是100%准确,但是我敢打赌,这非常接近,尤其是考虑到原始.NET框架中有多少基于Delphi。)
当您更改窗体的Width属性时,所有这些“副作用”都会发生。他们中的任何一个看起来不合适还是不正确?
看一下Microsoft设计规则,尤其是其中之一:
...如果存在以下条件之一,则方法是成为属性的一种很好的选择:
- 不带参数,并返回对象的状态信息。
- 接受单个参数来设置对象状态的某些部分。
属性应表现为字段。如果方法不能,则不应将其更改为属性。在以下情况下,方法胜于属性:
- 该方法执行耗时的操作。可以感觉到,该方法比设置或获取字段值所需的时间慢。
- 该方法执行转换。访问字段不会返回其存储的数据的转换版本。
- Get方法具有明显的副作用。检索字段的值不会产生任何副作用。
- 执行顺序很重要。设置字段的值不依赖于其他操作的发生。
- 连续两次调用该方法会产生不同的结果。
- 该方法是静态的,但返回一个可由调用者更改的对象。检索字段的值不允许调用者更改该字段存储的数据。
- 该方法返回一个数组...
除了前面提到的延迟加载外,还有日志功能。这些很少见,但并非不可能。
编程中几乎没有绝对值,为了回答您的问题,我们需要查看对象的某些理想属性,然后看看这是否适用于我们的情况
如果我们对某些问题回答是肯定的,那么仔细考虑一下特性及其副作用可能是个好主意。我认为,当您说副作用时,因为更新值本身就是副作用,所以它是指次要副作用。
通常,副作用越多,一堂课的考试就越困难。次要副作用越多,并发处理就越困难,但这是一个棘手的问题,无论如何它可能需要一些主要的设计思想。
大问题可能是针对那些将您的班级视为“黑匣子”的人,某些状态因为仅仅读了一个属性而改变,或者某些其他属性却因另一个改变而改变(这可能导致级联更新)。
因此,总的来说,我们不希望属性尽可能容易地推理和简单化,这在设计决策中就已隐含了,否则它将是一种方法。一个好的程序员总是可以违反规则,只是要确保您有一个非常非常好的理由:)