Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 条件属性是如何工作的?_C#_.net_.net 4.5_Il_Conditional Compilation - Fatal编程技术网

C# 条件属性是如何工作的?

C# 条件属性是如何工作的?,c#,.net,.net-4.5,il,conditional-compilation,C#,.net,.net 4.5,Il,Conditional Compilation,我有一些助手方法标记为[Conditional(“XXX”)]。其目的是使方法在仅存在XXX条件编译符号时进行条件编译。我们使用它来调试和跟踪功能,它工作得很好 在我研究条件编译如何工作的过程中,我发现了几个源代码,其中说明了带有条件属性标记的方法将被放置在IL中,但不会执行对这些方法的调用 如何将代码编译成IL而不执行?如何验证行为是否与所描述的一样?我在IL方面做得不多,所以我在这方面的技能有点弱。这是由编译器控制的。带有[Conditional]的所有方法仍将包含在MSIL中,但将包含一个

我有一些助手方法标记为
[Conditional(“XXX”)]
。其目的是使方法在仅存在XXX条件编译符号时进行条件编译。我们使用它来调试和跟踪功能,它工作得很好

在我研究条件编译如何工作的过程中,我发现了几个源代码,其中说明了带有
条件
属性标记的方法将被放置在IL中,但不会执行对这些方法的调用


如何将代码编译成IL而不执行?如何验证行为是否与所描述的一样?我在IL方面做得不多,所以我在这方面的技能有点弱。

这是由编译器控制的。带有
[Conditional]
的所有方法仍将包含在MSIL中,但将包含一个
.custom instance
行,详细说明
[Conditional]
。在方法调用方的编译时,编译器进行词法分析,然后进行语义分析和重载解析,并在您放置的
[Conditional]
方法中找到
.custom实例
IL。因此,它不会编译调用

因此:编译器编译目标方法,但不编译对该方法的任何调用。注意:该方法仍然存在,您仍然可以使用反射调用它。看

对条件方法的调用被包括或省略,这取决于此符号是否在调用点定义。如果定义了符号,则包括呼叫;否则,省略呼叫(包括对接收器和呼叫参数的评估)

你怎么能证实呢?启动开发人员命令提示符,键入
ildasm
,然后打开相关的DLL/EXE。检查调用方和被调用的
[Conditional]
方法。您将看到被调用的方法具有额外的IL和
.custom instance
,并且调用程序行在您期望的地方被省略。使用下面的代码在控制台应用程序上进行尝试

为什么??在某些情况下,它使条件调用比使用
#if
更简单。看

类程序
{
静态void Main(字符串[]参数)
{
AlwaysEmit();
DebugEmit();
VerboseEmit();
}
公共静态void AlwaysEmit()
{
Console.WriteLine(“向上传送我”);
}
[有条件的(“调试”)]
公共静态void DebugEmit()
{
Console.WriteLine(“柯克出局”);
}
[有条件的(“详细的”)]
公共静态void VerboseEmit()
{
控制台。WriteLine(“再说一遍?”);
}
}
在相应的MSIL中,包括了
verboseem
,但没有从
Main
调用:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       14 (0xe)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  call       void RateScope.SdrApi.UploaderConsoleApp.Program::AlwaysEmit()
  IL_0006:  nop
  IL_0007:  call       void RateScope.SdrApi.UploaderConsoleApp.Program::DebugEmit()
  IL_000c:  nop
  IL_000d:  ret
} // end of method Program::Main

...

.method public hidebysig static void  VerboseEmit() cil managed
{
  .custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string)
     = ( 01 00 07 56 45 52 42 4F 53 45 00 00 ) // ...VERBOSE..
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Say that again\?"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Program::VerboseEmit
加分。查看控制台输出和MSIL(相应地修改Emit方法):

(已经讨论过这是否是一个答案,但觉得值得一提。如果人们不同意DVs或评论,我很乐意删除)

这会影响调用站点而不是方法本身,这一事实的一个重要特性是,此特性可以跨程序集工作,并且影响调用是否被调用的是调用站点范围内的编译符号

因此,必须将实际方法发送到已编译程序集中的一个原因是,当时,实际上还不知道是否要调用该方法


在稍后的时间点,当消费应用程序被编译时,我们才知道是否使用了该方法。这也意味着,在一个复杂的解决方案中,在多个级别上有多个使用者,一些调用可能会发生(在某些项目中),而其他调用则不会发生。

如果您的条件调用不起作用,并且在VS:

确保在项目生成属性中添加调试编译符号:

您也可以在条件中的DEBUG中使用DEBUG,这样就可以了

static void Main(string[] args)
{
    int callCount = 0;
    AlwaysEmit(++callCount);
    VerboseEmit(++callCount);
    DebugEmit(++callCount);
    Console.WriteLine("Call count = " + callCount);
    Console.ReadLine();
}