2010-01-30 19 views
8

¿Es posible en C# escribir código MSIL que agregará una directiva de preprocesador al código, por ejemplo, #warning, si se cumple una determinada condición? O tal vez esto se puede hacer con reflexión, no sé.C#: escribir MSIL para agregar una directiva de preprocesador

Estoy tratando de escribir un atributo personalizado que, si se aplica incorrectamente al método o a la propiedad de una clase, generará una advertencia del compilador. Usar el atributo Obsolete existente no funcionará porque solo el uso de mi atributo personalizado causa la advertencia, y no quiero eso. Quiero que el constructor de atributo personalizado compruebe una condición, y si esa condición es verdadera, genere una advertencia de compilación.

Actualización: Después de leer mi pregunta, creo que lo que estoy pidiendo es imposible solo porque estoy mezclando restricciones de tiempo de ejecución y en tiempo de ejecución. Creo que terminaré con una tarea posterior a la compilación para verificar el archivo DLL recién creado y hacer que escuche los mensajes de error si la condición es verdadera.

+1

Me parece ver lo que está diciendo, usted quiere un atributo personalizado aplicado a un método, y emiten una directiva #warning si una condición es met..I no creo que se puede hacer eso ya que una #warning es una compilación directiva de tiempo .. – t0mm13b

+0

Buena pregunta por cierto ... +1 de mí ... – t0mm13b

Respuesta

6

Vi esta pregunta procedente de su hilo anterior. Para citar mal al gran Jamie Zawinski: "Algunas personas, cuando se enfrentan con un problema, piensan" Lo sé, usaré un atributo ". Ahora tienen dos problemas".

Un atributo es simplemente datos fuera de banda, compilados en los metadatos de un ensamblaje. No puede afectar la ejecución del programa o el comportamiento de la herramienta, a menos que el programa o la herramienta esté explícitamente programado para reconocer el atributo específico. Necesita hacerlo usando Reflection.

Lo que necesita hacer es escribir su propia herramienta. Se debe ejecutar después de que se construye un ensamblado, utilizando el paso Post-Build para un proyecto. Necesita cargar el ensamblaje y usar Reflection para repetir los tipos en el ensamblaje. Para cada tipo, itere los métodos con Type.GetMethods() y use MethodInfo.GetCustomAttributes() para descubrir y construir un atributo que podría haberse programado.

Puede usar Type.GetInterfaces() para descubrir qué interfaces se implementan por tipo. Ahora puede quejarse cuando ve que hay un método presente que implementa un método de interfaz pero le falta un atributo que así lo indique. Y su objetivo final: puede quejarse cuando vea un método con un atributo que dice que implementa un método de interfaz pero el tipo ya no lo hereda.

Use Environment.ExitCode para hacer que la herramienta falle la construcción si ve algo objetable. Esto se ocupa de la aplicación. Por cierto: los programadores realmente odian romper la construcción. Eso bien podría alentarlos a usar el atributo religiosamente. O podría animarlos a editar el paso de compilación posterior.

+0

Excelente respuesta, y la ruta que voy a seguir. No sé si debería elegirlo como la respuesta seleccionada a esta pregunta porque no responde directamente mi pregunta MSIL, pero está abordando el problema detrás de varias de mis preguntas recientes. –

+2

¿Estás diciendo que debería usar expresiones regulares en su lugar? ;) –

+0

@Remus: suena como si supieras la cita. Sarah es probablemente un s/he. No, la expresión regular no está indicada aquí. –

1

Probablemente podría hacer esto con un post-build task que refleje su código compilado y verifique la condición. No tengo ninguna experiencia creando tareas de MSBuild, pero es un lugar donde puedes comenzar.

Otra sugerencia una vez que se lanza .NET 4.0 es usar Code Contracts para especificar los requisitos para los parámetros del constructor del atributo. Esto sería capturado en tiempo de compilación.

2

el compilador almacena dos cosas para atributos personalizados:

  • El constructor de atributos para llamar
  • Los datos de cada parámetro para pasar al constructor

El constructor sólo se invoca cuando el la aplicación se está ejecutando y alguien llama a GetCustomAttributes para su Assembyl, Type, MethodInfo, ParameterInfo, etc.

Tiene s Otras opciones a considerar:

  • Escriba una tarea de MSBuild personalizada que se ejecute después de la etapa de compilación, cargue el ensamblado compilado y verifique el uso de atributos de la aplicación.
  • Utilice el atributo AttributeUsage para especificar los elementos de código a los que se puede aplicar el atributo.
  • Deferir la validación de atributo al tiempo de ejecución.
0

Sospecho que la respuesta sería no, no se puede, porque la directiva #warning es algo de CSC (es decir, se está ordenando al compilador que actúe de cierta manera). Al escribir MISL en bruto, obviamente, CSC no se mezcla, por lo tanto, no hay un compilador al que dirigir para hacer algo.

Básicamente, la directiva (como indican '#warning' es una instrucción para CSC a comportarse de una manera determinada en las condiciones especificadas.

2

En resumen, no. Directivas de pre-procesamiento no tienen representación IL, ya que solo existen como metadatos utilizados durante la compilación de un archivo fuente.

El tipo de cosas que está haciendo podría ser mejor que custom FxCop rule.

1

Mi intuición es que no se puede inyectar una directiva #warning basada en un atributo y condición personalizada, ya que el compilador la atrapa en tiempo de compilación, es como una situación de gallina y huevo ya que el atributo personalizado debería se evaluará primero antes de inyectar un #warning, pero para que eso suceda se debe llevar a cabo primero una acción de compilación.

Espero que esto ayude, Saludos cordiales, Tom.

2

¿Alguna vez has oído hablar de Boo? Tiene formas interesantes en las que puedes conectarte a la canalización del compilador. Una de estas características se llama syntactic attributes, que son atributos que implementan una interfaz que el compilador llama, para que puedan participar en la generación de código.

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

    [getter(LastName)] 
    _lname as string 

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

Los atributos en este código generarán captadores públicos para los campos y verificaciones nulas para los parámetros del constructor. Todo terminará en el ensamblado compilado.

I've always wanted este tipo de extensibilidad para ser parte del compilador de C#. Tal vez lo haga algún día. Hasta entonces, puede usar un compilador posterior, como CciSharp. CCiSharp reescribirá el CIL en función de los atributos especiales del ensamblaje, al igual que con los atributos Boo synctatic.

Teniendo en cuenta este código:

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

CCiSharp mute el código basado en la LazyAttribute a esto:

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 se basa en el proyecto Common Compiler Infrastructure, el mismo utilizado para implementar el puesto code contracts compilador en .NET Framework 4.0.

Así que esta es la forma en que puede mutar el CIL generado.

embargo, una directiva #warning no tiene una representación CIL, es sólo una directiva de compilación. Para agregar esta directiva, lo que debe mutar no es el CIL generado, sino el código C# en sí mismo. Tendría que implementar un analizador C# para eso. Creo que la mejor opción es, como se indica en otras respuestas, crear un evento posterior a la construcción que se refleje sobre el conjunto generado y emita la advertencia deseada.

Cuestiones relacionadas