8

我试图通过使用 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();
    }
}

有一个更好的方法吗?

4

5 回答 5

7

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

    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 的最佳想法,但在某些情况下这种模式很有用(我从一个不相关的用例中找到了我的方法)。

于 2016-08-25T05:42:28.693 回答
5

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

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

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

于 2011-03-21T10:43:34.710 回答
1

You should leave the task to the optimizer (or JIT compiler) and use:

interface IWhatever
{
  void Trace(string strWhatever);
}

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

Of neither the optimizer nor the JIT compiler removes the call you should write angry emails to those developpers ;).

于 2014-08-18T12:20:43.423 回答
1

那这个呢:

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
}
于 2011-03-09T07:36:50.690 回答
1

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

interface IFoo
{
    void DoIt();
}

class NullFoo : IFoo
{
    public void DoIt()
    {
      // do nothing
    }
}

class TraceFoo : IFoo
{
    public void DoIt()
    {
        Console.WriteLine("Did it.");
    }
}
于 2011-03-09T08:02:55.317 回答