1

我正在编写一个(非常小的)框架来检查方法的前置条件和后置条件。入口点是(它们很容易成为方法;没关系):

public static class Ensures {
    public static Validation That {
        get { ... }
    }
}

public static class Requires {
    public static Validation That {
        get { ... }
    }
}

显然,当方法没有问题时,检查后置条件可能会很昂贵,而且实际上并不是必需的。所以我想要一个像这样工作的方法:

public static class Ensures {
    [ConditionalCallingCode("DEBUG")]
    public static Validation ThatDuringDebug {
        get { ... }
    }
}

whereConditionalCallingCodeAttribute意味着这个方法应该只在调用代码用定义的 DEBUG 符号编译时运行。这可能吗?

我希望客户端代码如下所示:

public class Foo {
    public void Bar() {
        ... // do some work
        Ensures.That // do these checks always
            .IsNotNull(result)
            .IsInRange(result, 0, 100);

        Ensures.WhileDebuggingThat // only do these checks in debug mode
            .IsPositive(ExpensiveCalculation(result));

        return result;
    }
}

当然,我可以不提供WhileDebuggingThat。然后客户端代码将如下所示:

public class Foo {
    public void Bar() {
        ... // do some work
        Ensures.That // do these checks always
            .IsNotNull(result)
            .IsInRange(result, 0, 100);

        #ifdef DEBUG
        Ensures.That // only do these checks in debug mode
            .IsPositive(ExpensiveCalculation(result));
        #endif

        return result;
    }
}

如果没有其他办法,这是后备计划,但它非常严重地破坏了 DRY。

据我了解,标记WhileDebuggingThatwith[Conditional("DEBUG")]将发出(或不发出)此方法,具体取决于在库编译期间是否定义了 DEBUG,而不是引用该库的程序集。所以我可以这样做,然后编写文档告诉库用户将其代码的调试版本与库的调试版本链接起来,并将发布版本与发布版本联系起来。这对我来说并不是最好的解决方案。

最后,我可以告诉图书馆用户在他们的项目中定义这个类:

using ValidationLibrary;
public static class EnsuresWhileDebugging {
    [Conditional("DEBUG")]
    public static Validation That() {
        return Ensures.That;
    }
}

据我所知,这应该也可以,但仍然需要打破 DRY 原则,即使只是轻微的。

4

8 回答 8

3

除了处理属性而不是方法之外,这是普通ConditionalAttribute不能为您做的任何事情吗?您可能需要更改调用方式,以便获得方法而不是属性 - 它返回值的事实可能会导致问题。

如果你能展示你的框架是如何使用的,那将会很有帮助——目前我们还没有很多工作要做。

要考虑的另一件事是提供库的各种二进制版本 - 这样调用者就可以提供实际上不进行任何检查的不同版本。但是,仅凭您提供的代码很难分辨。

于 2008-12-27T11:43:06.403 回答
1

在这里找到的任何解决方案都会比实际检查慢。此外,由于它不会像 一样内置到编译器ConditionalAttribute中,因此仍会计算参数。如果后置条件可能非常复杂,例如

Ensures.That.IsPositive(ExpensiveCalculation(result));

您可能会考虑使用 icelava 的建议来反映调用程序集以查找它是在调试还是发布中构建的 - 但是您必须使用某种委托来延迟计算 - 以确保仅在需要时完成。例如:

Ensures.WhileDebugging.That. IsPositive(() => ExpensiveCalculation(result));

IsPositive 函数应该运行 lambda 并检查它的结果,只有在反射之后才能确定是否应该计算它。

于 2008-12-28T13:11:12.310 回答
0

我没有试过这个,因为我要洗澡离开家。

  1. 调用 Assembly.GetCallingAssembly() 以获取调用当前执行方法的方法(类)来自的程序集。
  2. 对该 Assembly 对象运行检查以查看它是 Release 还是 Debug build
于 2008-12-27T12:43:19.540 回答
0

听起来您正在做的大部分事情已经使用Debug.Assert().

就此而言,这段代码只会在调试模式下运行(但你必须忍受 catch-block 的缓慢):

try
{
     Debug.Assert(false);
}
catch (Exception e)
{
     // will only and always run in debug mode

}
于 2008-12-27T15:17:00.153 回答
0

看来我想要的只是不可用。我可能会满足于提供从Validationto的隐式转换bool,以便验证检查可以包含在Debug.Assert().

于 2008-12-29T12:16:54.663 回答
0

即使在程序编译后,也可以使用 bool 设置/更改 Debug Assert 方法,例如,如果该值来自项目用户设置:

Debug.Assert(!Properties.Settings.Default.UseAutoDebug);
于 2008-12-30T02:10:39.707 回答
0

我不确定,但我认为您可以为此使用 ConditionalAttribute:是否发出调用将取决于用户构建的类型,而不是您的库。您可以使用 Reflector 或 ILDasm 进行检查:编译您的示例并在 Reflector (ILDasm) 中查看示例项目中是否发出了调用。

于 2008-12-30T19:27:57.447 回答
0

我发生了这种情况:项目 A 调用 B 的 1 函数。B 包含此函数:Assembly.GetCallingAssembly().FullName 如果在模式调试时构建 B 然后运行,则此函数返回项目 A 的名称,如果在模式释放时构建而不是返回名称项目 B。我不知道发生这种情况的原因。请支持我谢谢

于 2009-06-16T07:12:49.520 回答