繁体   English   中英

C#:编写MSIL以添加预处理程序指令

[英]C#: writing MSIL to add a preprocessor directive

是否有可能在C#中编写MSIL代码,将代码添加预处理程序指令,例如#warning ,如果满足某个条件? 或许这可以通过反思完成,我不知道。

我正在尝试编写一个自定义属性,如果错误地应用于类的方法或属性,将生成编译器警告。 使用现有的Obsolete属性将不起作用,因为只使用我的自定义属性会导致警告,我不希望这样。 我希望自定义属性构造函数检查条件,如果该条件为true,则导致编译警告。

更新:在回顾了我的问题之后,我认为我所要求的是不可能的,因为我正在混合编译时和运行时约束。 我想我最终将使用一个后期构建任务来检查刚刚构建的DLL,如果条件为真,它会发出错误消息。

我看到这个问题来自你以前的帖子。 错误引用伟大的Jamie Zawinski:“有些人在遇到问题时会想”我知道,我会使用一个属性。“现在他们有两个问题”。

属性仅仅是带外数据,编译为程序集的元数据。 除非程序或工具明确编程为识别特定属性,否则它不会影响程序执行或工具行为。 它需要使用Reflection来实现。

你需要做的是编写自己的工具。 它应该在构建程序集后执行,使用项目的Post-Build步骤。 它需要加载程序集并使用Reflection来迭代程序集中的类型。 对于每种类型,使用Type.GetMethods()迭代方法,并使用MethodInfo.GetCustomAttributes()来发现和构造可能已编程的属性。

您可以使用Type.GetInterfaces()来发现类型实现的接口。 您现在可以在看到存在实现接口方法但缺少属性的方法时抱怨。 并且你的最终目标是:当你看到一个带有一个属性的方法时,你会抱怨它说它实现了一个接口方法,但是类型不再继承它。

如果您发现任何令人反感的事情,请使用Environment.ExitCode使该工具失败。 这需要执行。 顺便说一句:程序员真的很讨厌打破构建。 这可能会鼓励他们虔诚地使用这个属性。 或者它可能会鼓励他们编辑后期构建步骤。

编译器为自定义属性存储两件事:

  • 要调用的属性构造函数
  • 要传递给构造函数的每个参数的数据

构造函数仅在应用程序运行时调用,并且有人为您的Assembyl,Type,MethodInfo,ParameterInfo等调用GetCustomAttributes。

您还有其他一些选择:

  • 编写在编译阶段之后运行的自定义MSBuild任务,加载已编译的程序集并检查应用程序的属性用法。
  • 使用AttributeUsage属性指定可以应用该属性的代码项。
  • 将属性验证推迟到运行时。

简而言之,没有。 预处理指令没有IL表示,因为它们仅作为在编译源文件期间使用的元数据存在。

作为自定义FxCop规则 ,您正在做的事情可能会更好。

你听说过Boo吗? 它有一些有趣的方法可以连接到编译器管道。 一个这样的特性称为语法属性 ,它是实现编译器调用的接口的属性,以便它们可以参与代码生成。

class Person:
  [getter(FirstName)]
  _fname as string

  [getter(LastName)]
  _lname as string

  def constructor([required] fname, [required] lname):
    _fname = fname
    _lname = lname

此代码中的属性将为字段生成公共getter,并为构造函数参数进行null检查。 它将最终都在编译的程序集中。

我一直希望这种可扩展性成为C#编译器的一部分。 也许有一天会。 在此之前,您可以使用后编译器,如CciSharp CCiSharp将根据程序集中的特殊属性重写CIL ,就像Boo synctatic属性一样。

鉴于此代码:

class Foo {
  [Lazy]
  public int Value { 
    get { return Environment.Ticks; } 
  }
}

CCiSharp会产生变异基础上的代码LazyAttribute这样:

class Foo {
  int Value$Value; // compiler generated
  int Value$Initialized;
  int GetValueUncached() { 
    return Environment.Ticks;
  }
  public int Value  {
    get {
      if(!this.Value$Initialized) {
        this.Value$Value = this.GetValueUncached();
        this.Value$Initialized = true;
      }
      return this.Value$Value;
    }
}

CCiSharp基于Common Compiler Infrastructure项目,同样用于在即将推出的.NET Framework 4.0中实现编译后的代码契约

所以这就是你如何改变生成的CIL。

但是,# #warning指令没有CIL表示,它只是一个编译器指令。 要添加此指令,必须改变的不是生成的CIL,而是C#代码本身。 你必须为它实现一个C#解析器。 我认为,如其他回复中所述,最佳选择是创建一个后期构建事件,该事件将反映生成的程序集并发出所需的警告。

您可以使用反映编译代码并检查条件的构建后任务来执行此操作。 我没有任何创建MSBuild任务的经验,但它是一个你可以开始的地方。

.NET 4.0发布后的另一个建议是使用代码约定来指定属性构造函数参数的要求。 这将在编译时捕获。

我的直觉是,你不能根据自定义属性和条件注入一个#warning指令,因为编译器会在编译时捕获它,它就像鸡和鸡蛋一样,因为自定义属性必须先评估注入一个#warning但为了实现这一点,必须首先执行编译时操作。

希望这会有所帮助,最好的问候,汤姆。

我怀疑答案是否定的,你不能,因为#warning指令是一个CSC的东西(即你指示编译器以某种方式行事)。 在编写原始MISL时,显然,CSC没有混合,因此没有编译器可以指导做某事。

基本上a指令(表示'#warning'是CSC在特定条件下以某种方式表现的指令。

暂无
暂无

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

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