【问题标题】:C# conditional attribute on interface member接口成员上的 C# 条件属性
【发布时间】:2011-03-09 05:22:02
【问题描述】:

我正在尝试通过使用 Conditional 属性来摆脱代码中的“#if TRACE”指令,但不能轻松地将这种方法应用于接口。我有办法解决这个问题,但它非常难看,我正在寻找更好的解决方案。

例如我有一个带有条件编译方法的接口。

interface IFoo
{
#if TRACE
    void DoIt();
#endif
}

我不能在接口中使用条件属性:

// Won't compile.
interface IFoo
{
    [Conditional("TRACE")]
    void DoIt();
}

我可以让接口方法只调用具体类中的条件私有方法:

interface IFoo
{
    void TraceOnlyDoIt();
}

class Foo : IFoo
{
    public void TraceOnlyDoIt()
    {
        DoIt();
    }

    [Conditional("TRACE")]
    void DoIt()
    {
        Console.WriteLine("Did it.");
    }
}

这将使我的客户端代码在非 TRACE 构建中对“nop”TraceOnlyDoIt() 方法进行冗余调用。我可以通过接口上的条件扩展方法来解决这个问题,但它变得有点难看。

interface IFoo
{
    void TraceOnlyDoIt();
}

class Foo : IFoo
{
    public void TraceOnlyDoIt()
    {
        Console.WriteLine("Did it.");
    }
}

static class FooExtensions
{
    [Conditional("TRACE")]
    public static void DoIt(this IFoo foo)
    {
        foo.TraceOnlyDoIt();
    }
}

有没有更好的方法来做到这一点?

【问题讨论】:

  • 在这里使用partial methods 对您有用吗?
  • 我觉得如果您尝试对接口执行此操作,您可能在某处有一个泄漏的抽象。跟踪信息可能更多的是实现细节而不是合同细节。
  • @ROMANARMY:是的,这是一个非常糟糕的界面。我只是想一步一步地对其进行消毒。
  • +1 @ROMANARMY。跟踪方法不应出现在接口上,因为它是实现细节。但是,如果您坚持使用它,我会使用您开始使用的 #if ... #endif 方法。
  • @Xcaliburp。是的,我想我会坚持使用#if,直到我完全从界面中删除与跟踪相关的内容。 (如果您在回复中发布,我会接受。)

标签: c# conditional-attribute


【解决方案1】:

我喜欢扩展方法方法。它可以做得更好/更健壮,至少对调用者来说:

    public interface IFoo
    {
        /// <summary>
        /// Don't call this directly, use DoIt from IFooExt
        /// </summary>
        [Obsolete]
        void DoItInternal();
    }

    public static class IFooExt
    {
        [Conditional("TRACE")]
        public static void DoIt<T>(this T t) where T : IFoo
        {
#pragma warning disable 612
            t.DoItInternal();
#pragma warning restore 612
        }
    }

    public class SomeFoo : IFoo
    {
        void IFoo.DoItInternal() { }

        public void Blah()
        {
            this.DoIt();
            this.DoItInternal(); // Error
        }
    }

通用类型约束用于避免虚拟调用和潜在的值类型装箱:优化器应该很好地处理这个问题。至少在 Visual Studio 中,如果您调用内部版本,因​​为已过时,它会生成警告。显式接口实现用于防止意外调用具体类型的内部方法:用 [Obsolete] 标记它们也可以。

虽然这可能不是 Trace 的最佳想法,但在某些情况下这种模式很有用(我从一个不相关的用例中找到了我的方法)。

【讨论】:

    【解决方案2】:

    跟踪方法不应出现在接口上,因为它是实现细节。

    但是,如果您被界面卡住并且无法更改它,那么我会使用您开始使用的#if ... #endif 方法。

    虽然这是一个相当野蛮的语法,所以我很同情你为什么要避免它......

    【讨论】:

      【解决方案3】:

      这个呢:

      interface IFoo
      {
        // no trace here
      }
      
      class FooBase : IFoo
      {
      #if TRACE
          public abstract void DoIt();
      #endif
      }
      
      class Foo : FooBase
      {
      #if TRACE
          public override void DoIt() { /* do something */ }
      #endif
      }
      

      【讨论】:

      • 但是现在他必须将所有对 IFoo 的引用更改为 FooBase 才能使用他的条件方法...
      【解决方案4】:

      我建议您改用null object pattern。我将条件语句视为一种代码气味,因为它们隐藏了真正的抽象。是的,您将获得一些额外的方法调用,但这些方法实际上对性能没有影响。在跟踪构建中,您可以注入 TraceFoo,例如通过配置文件。这也将使您能够在非跟踪构建上启用它。

      interface IFoo
      {
          void DoIt();
      }
      
      class NullFoo : IFoo
      {
          public void DoIt()
          {
            // do nothing
          }
      }
      
      class TraceFoo : IFoo
      {
          public void DoIt()
          {
              Console.WriteLine("Did it.");
          }
      }
      

      【讨论】:

      • 我猜来自 OP 的 IFoo 实际上除了他想要保留在界面上的 DoIt 方法之外还有一些行为。在这种情况下,传递一个“Null 对象”而不是具体类会失去一些他想要保留的行为。
      【解决方案5】:

      您应该将任务留给优化器(或 JIT 编译器)并使用:

      interface IWhatever
      {
        void Trace(string strWhatever);
      }
      
      class CWhatever : IWhatever
      {
          public void Trace(string strWhatever)
          {
      #if TRACE
             // Your trace code goes here
      #endif
          }
      }
      

      无论是优化器还是 JIT 编译器都不会删除您应该向这些开发人员写愤怒电子邮件的调用;)。

      【讨论】:

        猜你喜欢
        • 2011-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-26
        • 2016-01-28
        • 2013-01-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多