接口成员上的C#条件属性

接口成员上的C#条件属性,c#,conditional-attribute,C#,Conditional Attribute,我试图通过使用条件属性来摆脱代码中的“#if TRACE”指令,但无法将此方法轻松应用于接口。我有办法解决这个问题,但这很难看,我正在寻找更好的解决办法 例如,我有一个带有条件编译方法的接口 interface IFoo { #if TRACE void DoIt(); #endif } 我无法在接口中使用条件属性: // Won't compile. interface IFoo { [Conditional("TRACE")] void DoIt(); } 我可以

我试图通过使用条件属性来摆脱代码中的“#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.");
    }
}
这将使我的客户端代码在非跟踪构建中对'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();
    }
}
有更好的方法吗?

这个怎么样:

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
}

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

interface IFoo
{
    void DoIt();
}

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

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

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

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


不过,这是一种相当野蛮的语法,因此我同意您可能希望避免它的原因…

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


优化器和JIT编译器都不会删除调用,您应该向这些开发人员发送愤怒的电子邮件;)

我喜欢扩展方法。至少对于呼叫者来说,它可以变得更好/更健壮:

    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
        }
    }
公共接口IFoo
{
/// 
///不要直接调用它,使用IFooExt中的DoIt
/// 
[过时]
void DoItInternal();
}
公共静态类IFooExt
{
[有条件的(“跟踪”)]
公共静态void DoIt(这个T),其中T:IFoo
{
#pragma警告禁用612
t、 DoItInternal();
#pragma警告恢复612
}
}
公共类SomeFoo:IFoo
{
void IFoo.DoItInternal(){}
公开无效的废话
{
这是DoIt();
this.DoItInternal();//错误
}
}
泛型类型约束用于避免虚拟调用和可能的值类型装箱:优化器应该很好地处理这个问题。至少在VisualStudio中,如果因为过时而调用内部版本,它会生成警告。显式接口实现用于防止意外调用具体类型上的内部方法:使用[Observe]标记它们也有效


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

在这里使用对您有用吗?我觉得如果您试图对接口这样做,您可能会在某个地方有一个泄漏的抽象。跟踪信息可能更像是一个实现细节,而不是一个合同细节。@ROMANARMY:是的,这是一个非常糟糕的界面。我只是试着一步一步地清理它。跟踪方法不应该出现在接口上,因为它是一个实现细节。但如果你坚持下去,我会用#如果#您开始使用的endif方法。@Xcaliburp。是的,我想我会坚持使用#if's,直到我从界面上完全删除了与跟踪相关的内容。(如果你在回复中发布,我会接受。)但现在他必须将所有对IFoo的引用更改为FooBase,以便使用他的条件方法……我猜OP中的IFoo实际上有一些行为,而不是DoIt方法,他希望保留在接口上。在这种情况下,传递一个“Null对象”而不是具体的类将丢失一些他想要保留的行为。
    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
        }
    }