【问题标题】:Why does .NET's Conditional Attribute Cause Side-Effects to be Removed?为什么 .NET 的条件属性会导致副作用被移除?
【发布时间】:2009-01-04 11:43:20
【问题描述】:

我今天读到了Conditional 属性。根据 MSDN:

ConditionalAttribute 应用于方法向编译器表明,除非定义了与ConditionalAttribute 关联的条件编译符号,否则不应将对该方法的调用编译为Microsoft 中间语言(MSIL)。

好的。这很清楚。所以对方法的调用不会被编译。但是副作用呢?

[Conditional("UndefinedCondition")]
static void f1(int x) { Console.WriteLine(x); }

static int a = 0;
static void f2() { f1(++a); }

所以当f2 被调用时,对f1 的调用应该被移除。但是为什么++a 也被删除了?这对我来说没有任何意义!

【问题讨论】:

    标签: .net conditional


    【解决方案1】:

    扩展 Marc 的答案。

    这绝对是“设计使然”。理解这一点的合理化的最好方法是考虑这段代码取代了什么。这个特性以许多更简洁的方式采用了条件编译代码的方式。

    例如,

    #if DEBUG
    f1(++a);
    #endif
    

    或其他版本

    #define f1(x) ...
    

    在非调试情况下,显然没有副作用。这与 [Conditional] 代码的行为相同。我同意这绝对不像第一个例子那么清楚,但它和第二个例子一样清楚。

    【讨论】:

      【解决方案2】:

      是的,参数所需的任何调用也被删除。这意味着对于典型的用例(调试版本),您需要删除整个表达式,这通常是预期的。

      基本上,在使用 [Conditional] 方法时需要非常小心,(在 C# 3.0 中)部分方法 - 如果部分方法的另一半没有实现。作为示例(对于部分方法),请参见下文。请注意,对HasSideEffect() 的调用已被删除(取消注释Bar 的另一半以查看它是否有效):

      using System;
      partial class Foo {
          partial void Bar(string value);
          static void Main() {
              Foo foo = new Foo();
              foo.Bar(HasSideEffect());
          }
          static string HasSideEffect() {
              Console.WriteLine("hello");
              return "world";
          }
      }
      
      partial class Foo {
          /* uncomment this
          partial void Bar(string value) {
              Console.WriteLine(value);
          }*/ 
      }
      

      【讨论】:

      • 谢谢。你说他们有“非常相似”的行为。有区别吗?
      • 并非如此——除了包含的条件不同; with [Conditional] 它是一个符号 - 对于部分方法,它是是否实现部分方法。
      【解决方案3】:

      我猜这是因为编译器实现的简单性。

      无论如何我都会害怕这样的代码(即使它按你期望的那样工作)并将它写成

      ++a;
      f1(a);
      

      为了清楚起见。您始终可以看到执行的内容和未执行的内容。

      【讨论】:

      • 那太好了。但是如果 f1() 最初是一个普通的方法,那么有人决定让它有条件,那么这会破坏应用程序,没有任何编译器警告,不是吗?我会说编译器应该执行副作用,因为“++a”本身不是有条件的!
      • 否则,我相信编译器会破坏一致性。只有我标记为条件的代码应该被删除,而不是我没有标记为条件的任何其他代码!
      • 这样你就可以选择了。将您一直想要执行的代码移出,如果仅在方法调用完成时才应该执行,则将其保留。是的,添加此属性的程序员可能会破坏事情,但程序员也需要承担一些责任。
      • 不是未编译的函数,而是调用。但是,当您的编译器应该删除 IL 调用时,它似乎删除了 C# 调用。也许您应该报告此行为,以便他们修复它。
      • 现在我阅读了它,如果您不重新编译您的应用程序,但有人在另一个程序集中设置了某些函数的条件,它实际上不会中断。它会调用它:它是删除调用的编译器。功能还在。 (我没有对此进行测试,我只是在解释文档)。
      【解决方案4】:

      我还怀疑这种行为被设计为与 C 预处理器宏相同,后者通常设置为在不使用时不评估参数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-27
        • 1970-01-01
        相关资源
        最近更新 更多