【问题标题】:Why does conditional methods must have a return type of void?为什么条件方法的返回类型必须是 void?
【发布时间】:2017-09-26 11:59:35
【问题描述】:

我们正在为我们的 API 编写一些全局异常处理程序,我们只需要在 Debug 配置中执行某些方法,显然条件方法是可能的解决方案。

我看到的是 [Conditional("DEBUG")] 只能用于返回类型为 void 的方法,如 MSDN 所说:

条件方法受以下限制:

  • 条件方法必须是类或结构中的方法 宣言。如果 Conditional 属性发生编译时错误 在接口声明中的方法上指定。
  • 条件方法的返回类型必须为 void
  • 条件方法不得使用覆盖修饰符进行标记。然而,条件方法可以用 virtual 修饰符标记。这种方法的覆盖是隐含的条件,并且不能用条件属性显式标记。
  • 条件方法不能是接口方法的实现。否则,会发生编译时错误。

来源:https://msdn.microsoft.com/en-us/library/aa664622(v=vs.71).aspx

我们知道标有ConditionalAttribute 的方法会到达IL,那么为什么CLR 不简单地拦截返回default(T) 的方法调用,例如因此允许具有任何类型的返回类型的方法是有条件的?还是我遗漏了什么?

【问题讨论】:

  • 如果方法返回一些东西,那么省略调用会改变代码的行为,这是不可取的。 void 方法在其调用中没有本地结果,可以可靠地省略它。
  • @David,我不明白省略对返回某些内容的方法的调用将如何改变代码的行为,它仍然会返回某些内容(对于发布和实际而言,它将是默认值(T)调试值),为什么不呢?
  • @Bathsheba:我明白你在说什么,虽然我不确定我是否能真正表达清楚。我很想从世界各地的 Eric Lipperts 那里得到类似这样的意见。
  • @David:这个 Eric Lippert 的观点是你的答案非常好,尽管你没有指出 void 表达式的一个重要特征,我将在评论中发表。我会让 Eric Lipperts 的其他人为自己说话。

标签: c# .net runtime clr language-design


【解决方案1】:

为什么 CLR 不简单地拦截返回 default(T) 的方法调用

更重要的是......为什么应该发生这种情况?如果没有令人信服的理由这样做,那么就没有令人信服的理由投入大量精力、工时、成本等来讨论、设计、考虑所有可能的场景、开发、测试并继续在该语言的所有未来版本中都支持它。对于一个本来就不需要的功能来说,这是一个相当大的节省。

void 方法保证没有结果。所以已经存在一个逻辑保证,即没有任何东西依赖它的结果。返回值的方法不能提供这样的保证。

由于没有任何东西依赖于其结果,因此省略它不会改变代码/逻辑的直接本地行为。而如果某件事确实依赖于它的结果(或者至少可能依赖于它的结果),那么本地行为可能会改变,这是不可取的。这种情况可能会引入任何数量的意外变化。甚至像返回 null 这样简单的事情,代码并不期望 null

一个人可能会坚持认为自己可以考虑自己的逻辑,并在编译器可以保证的范围之外做出自己的保证。 (类型转换和反射之类的东西对于开发人员来说可能是有用的工具,只要他们能够保持他们认为可以的稳定性。)但是在这种特殊情况下,很快会成为开发代码和生产代码不再做同样的事情。他们可能会做非常相似的事情,但不再是同一件事。这是一个重大问题,该语言试图避免这种情况。

这与系统可以做什么无关。我敢肯定,鉴于当时存在的信息,任何给定语言的任何给定版本可以支持无穷无尽的东西。但是应该吗?这完全是另一个问题。在这种情况下,支持这一点的成本似乎将大大超过从中获得的收益。

【讨论】:

  • 方法无效并不一定意味着它不会改变代码的行为。 void 方法通常被描述为所谓的“查询”,它们不会返回任何东西,但它们可以改变传递的状态,所以我不完全同意你对这部分的看法,除非你证明我错了,否则关于计划和我期望的东西每次我挖掘为什么 CLR 不提供某些东西时,我都会给出这样的答案,这是最终的答案,所以我并不感到惊讶。
  • @kuskmen:状态变化和结果是两件不同的事情。是的,它可能会改变系统其他地方的状态,例如修改提供对象的属性。任何代码都可以做到这一点。但是在本地,它保证没有结果,也不依赖于该结果。编译器一般不太关心是否存在可以改变状态的业务逻辑,而更关心是否可以保证本地和结构逻辑工作。
  • 我想你是对的,只是我没有看到函数的默认返回类型会如何影响结构逻辑,也许我需要在聊天中进行一些详细说明,所以我们不会发送垃圾邮件,但我明白了既然你有道理。
  • 将此功能限制为 void 方法的另一个不错的特性是,此类表达式可以出现的位置数量极其有限,因此必须是考虑和测试也是有限的。 M() 形式的表达式,其中 M 为 void,只能在允许 statement 的上下文中使用,这意味着我们不必担心像 class C { int x = M(); } 这样的情况 - M () 不能被删除,因为首先在这里有一个 void 表达式是不合法的。
猜你喜欢
  • 2015-06-29
  • 2012-09-13
  • 2011-10-24
  • 2014-04-15
  • 2015-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
相关资源
最近更新 更多