简体   繁体   English

接口成员的C#条件属性

[英]C# conditional attribute on interface member

I'm trying to get rid of the "#if TRACE" directives in my code, by using the Conditional attribute instead, but can't apply this approach easily to interfaces. 我试图通过使用Conditional属性来摆脱我的代码中的“#if TRACE”指令,但是不能轻易地将这种方法应用于接口。 I have a way round this but it's pretty ugly, and I'm looking for a better solution. 我有一个方法,但它很丑,我正在寻找一个更好的解决方案。

Eg I have an interface with a conditionally compiled method. 例如,我有一个带有条件编译方法的接口。

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

I can't use the conditional attribute in an interface: 我不能在接口中使用条件属性:

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

I could have the interface method just call a conditional private method in the concrete class: 我可以让接口方法在具体类中调用一个条件私有方法:

interface IFoo
{
    void TraceOnlyDoIt();
}

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

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

This would leave my client code with redundant calls to the 'nop' TraceOnlyDoIt() method in a non-TRACE build. 这将使我的客户端代码在非TRACE构建中对“nop”TraceOnlyDoIt()方法进行冗余调用。 I can get round that with a conditional extension method on the interface, but it's getting a bit ugly. 我可以通过接口上的条件扩展方法来解决这个问题,但它有点难看。

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();
    }
}

Is there a better way to do this? 有一个更好的方法吗?

A trace method shouldn't be appearing on an interface as it's an implementation detail. 跟踪方法不应出现在接口上,因为它是一个实现细节。

But if you're stuck with the interface, and can't change it, then I'd use the #if ... #endif approach that you started with. 但是如果你坚持使用界面,并且无法改变它,那么我将使用你开始使用的#if ... #endif方法。

It is a rather savage syntax though, so I sympathise with why you might want to avoid it... 这是一个相当野蛮的语法,所以我同情你为什么要避免它......

I like the extension method approach. 我喜欢扩展方法方法。 It can be made a bit nicer/robust, at least for callers: 它可以更好/更健壮,至少对于呼叫者:

    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
        }
    }

Generic type constraints are used to avoid the virtual call and potential boxing of value types: the optimizer should deal with this well. 通用类型约束用于避免虚拟调用和值类型的潜在装箱:优化器应该很好地处理这个问题。 At least in Visual Studio it generates warnings if you call the internal version via because of Obsolete. 至少在Visual Studio中,如果由于Obsolete而调用内部版本,则会生成警告。 An explicit interface implementation is used to prevent accidentally calling the internal methods on the concrete types: marking them with [Obsolete] also works. 显式接口实现用于防止意外调用具体类型的内部方法:使用[Obsolete]标记它们也有效。

While this may not be the best idea for Trace stuff, there are some cases where this pattern is useful (I found my way here from an unrelated use case). 虽然这可能不是Trace的最佳选择,但在某些情况下这种模式很有用(我从不相关的用例中找到了我的方法)。

You should leave the task to the optimizer (or JIT compiler) and use: 您应该将任务留给优化器(或JIT编译器)并使用:

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 ;). 无论是优化器还是JIT编译器都不会删除调用,您应该向那些开发人员写下愤怒的电子邮件;)。

What about this: 那这个呢:

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
}

I would suggest you to use the null object pattern instead. 我建议你改用null对象模式 I view conditional statements as a kind of code smell because they hide real abstractions. 我将条件语句视为一种代码气味,因为它们隐藏了真正的抽象。 Yes you will get some extra method calls but these virtually have no impact on performance. 是的,您将获得一些额外的方法调用,但这些实际上对性能没有影响。 In trace builds you can inject the TraceFoo, via a config file for example. 在跟踪构建中,您可以通过配置文件注入TraceFoo。 This will also give you the capability to be able to enable it on non-trace builds as well. 这也使您能够在非跟踪构建上启用它。

interface IFoo
{
    void DoIt();
}

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM