我有以下代码在调试模式下将调试信息添加到消息中:
测试器中的代码dll
private string GetMessage(Foo foo)
{
var messageBuilder = new StringBuilder();
// add message info
#if DEBUG
messageBuilder.AppendLine(foo.GetDebuggingInfo());
#endif
return messageBuilder.ToString();
}
测试中的库
c 库通过托管C 包装器项目
从C#拨打电话#ifdef DEBUG
string Foo::GetDebuggingInfo()
{
...
}
#endif
现在,我遇到的问题是Foo
在一个单独的库中声明,并且GetDebuggingInfo
也有条件地编译为。为向后兼容的原因,测试人员DLL均与正在测试的库的所有先前发布的版本相对,以确保确保输出我们的服务器发送将继续如预期的所有不同版本的客户端摄入。然后将测试代码与要测试的库的所需版本一起复制到文件夹中,而不是对测试器进行编译的版本。一切运行正常,直到我的代码达到GetMessage
,那是我遇到MethodNotFoundException
的时候。
我第一次尝试解决这个问题,我想使用反射来确保在调用功能之前存在该功能,例如:
#if DEBUG
if (foo.GetType().GetMethod("GetDebuggingInfo", BindingFlags.Instance | BindingFlags.Public) != null)
messageBuilder.AppendLine(foo.GetDebuggingInfo());
#endif
我认为,即使Tester DLL是调试版本,并且测试的DLL是发行版本,也可以防止该方法被调用。仍然同样的问题。添加了一些其他跟踪调试语句后,我发现GetMessage
甚至没有进入。意识到这一点后,我尝试将有问题的方法调用到其自身的功能中,一切都起作用。
#if DEBUG
// reflection check remains to keep the call from blowing
// up when Tester is DEBUG and Under Test is RELEASE
if (foo.GetType().GetMethod("GetDebuggingInfo", BindingFlags.Instance | BindingFlags.Public) != null)
AddDebuggingInfo(messageBuilder, foo);
#endif
#if DEBUG
private void AddDebuggingInfo(StringBuilder messageBuilder, Foo foo) =>
messageBuilder.AppendLine(foo.GetDebuggingInfo());
#endif
因此,即使我有一个工作解决方案满足我的需求,我也不知道我的最终解决方案是如何工作的,因为我认为反射检查足以防止代码抛出MethodNotFoundException
。因此,在运行时,方法何时查找并解决?
当该对象的容器被jited时,所有对象都会获得"绑定",通常在第一次执行该对象时会被jited。
当您移动getDebuggingInfo时,只有在您实际输入AddDebuggingInfo方法的情况下,才会尝试绑定到GetDebuggingInfo的电话。
。另一个解决方法是,如果您声明变量dynamic
#if DEBUG
dynamic tmpFoo = foo;
if (foo.GetType().GetMethod("GetDebuggingInfo", BindingFlags.Instance | BindingFlags.Public) != null)
messageBuilder.AppendLine(tmpFoo.GetDebuggingInfo());
#endif
动态导致绑定在函数的呼唤中发生,而不是在包含函数的输入中发生。
另一个选择是使用您要检查的null以调用的MethodInfo
。
#if DEBUG
var info = foo.GetType().GetMethod("GetDebuggingInfo", BindingFlags.Instance | BindingFlags.Public);
if (info != null)
{
var text = (string)info.Invoke(foo, null);
messageBuilder.AppendLine(text);
}
#endif
看一下[ConditionalAttribute]
它可以执行您的#if .... #endif
代码所做的事情,但它还可以删除对装饰方法的调用。
一个限制:装饰方法必须返回void
。因此,您最终将StringBuilder
作为参数传递。
将有条件的属性应用于一种方法向编译器指示该方法的呼叫不应将其汇编为Microsoft中间语言(MSIL),除非定义与条件载体相关的条件编译符号。如果将此属性应用于不返回void的方法,则将在Visual Studio中遇到汇编错误。
static void Main()
{
var sb = new StringBuilder();
Console.WriteLine("Calling AppendDebugInfo1");
AppendDebugInfo1(sb);
Console.WriteLine("Calling AppendDebugInfo2");
AppendDebugInfo2(sb);
Console.WriteLine(sb);
}
[Conditional("DEBUG")]
public static void AppendDebugInfo1(StringBuilder sb)
{
sb.AppendLine("DEBUG is defined");
}
[Conditional("DEBUG"), Conditional("SUPERDEBUG"))]
public static void AppendDebugInfo2(StringBuilder sb)
{
sb.AppendLine("DEBUG or SUPERDEBUG is defined! whoaaaaa!");
}