2009-04-15 11 views
17

En otras palabras, podría ser posible crear un ensamblado, que ni siquiera compila (suponiendo que el código de comprobación no se elimine) si cada uno de las clases no tienen ("deben tener") atributos personalizados (por ejemplo, autor y versión)?¿Es posible consultar Atributos personalizados en C# durante el tiempo de compilación (no en tiempo de ejecución)

Este es el código que he utilizado para realizar consultas en tiempo de ejecución:

using System; 
using System.Reflection; 
using System.Collections.Generic; 


namespace ForceMetaAttributes 
{ 

    [System.AttributeUsage (System.AttributeTargets.Method, AllowMultiple = true)] 
    class TodoAttribute : System.Attribute 
    { 
     public TodoAttribute (string message) 
     { 
      Message = message; 
     } 
     public readonly string Message; 

    } 

    [System.AttributeUsage (System.AttributeTargets.Class | 
     System.AttributeTargets.Struct, AllowMultiple = true)] 
    public class AttributeClass : System.Attribute 
    { 
     public string Description { get; set; } 
     public string MusHaveVersion { get; set; } 


     public AttributeClass (string description, string mustHaveVersion) 
     { 
      Description = description; 
      MusHaveVersion = mustHaveVersion ; 
     } 

    } //eof class 


    [AttributeClass("AuthorName" , "1.0.0")] 
    class ClassToDescribe 
    { 
     [Todo (" A todo message ")] 
     static void Method() 
     { } 
    } //eof class 

    //how to get this one to fail on compile 
    class AnotherClassToDescribe 
    { 

    } //eof class 

class QueryApp 
{ 
     public static void Main() 
     { 

       Type type = typeof(ClassToDescribe); 
       AttributeClass objAttributeClass; 


       //Querying Class Attributes 

       foreach (Attribute attr in type.GetCustomAttributes(true)) 
       { 
         objAttributeClass = attr as AttributeClass; 
         if (null != objAttributeClass) 
         { 
           Console.WriteLine("Description of AnyClass:\n{0}", 
                    objAttributeClass.Description); 
         } 
       } 



       //Querying Class-Method Attributes 

       foreach(MethodInfo method in type.GetMethods()) 
       { 
         foreach (Attribute attr in method.GetCustomAttributes(true)) 
         { 
           objAttributeClass = attr as AttributeClass; 
           if (null != objAttributeClass) 
           { 
             Console.WriteLine("Description of {0}:\n{1}", 
                      method.Name, 
                      objAttributeClass.Description); 
           } 
         } 
       } 
       //Querying Class-Field (only public) Attributes 

       foreach(FieldInfo field in type.GetFields()) 
       { 
         foreach (Attribute attr in field.GetCustomAttributes(true)) 
         { 
           objAttributeClass= attr as AttributeClass; 
           if (null != objAttributeClass) 
           { 
             Console.WriteLine("Description of {0}:\n{1}", 
                      field.Name,objAttributeClass.Description); 
           } 
         } 
       } 
       Console.WriteLine ("hit Enter to exit "); 
       Console.ReadLine(); 
     } //eof Main 
} //eof class 

} //eof namespace 


//uncomment to check whether it works with external namespace 
//namespace TestNamespace { 

// class Class1 { } 
// class Class2 { } 

//} 

Editar: Sólo para justificar mi elección para la respuesta. Creo que CasperOne brindó la respuesta correcta a la pregunta.

Sin embargo, las razones para hacer la pregunta parecían ser weak. Probablemente debería comenzar a utilizar alguna herramienta externa, como: FinalBuilder o crear pruebas de control de cada uno para este "requisito", usando Pex, Nunit u otros marcos de pruebas unitarias ...

EDITAR he añadido una pequeña code snippet de un programa de consola al final de las respuestas que realiza el control ... no dude en comentar, criticar o sugerir mejoras
Una vez más me di cuenta de que este "requisito" debe implementarse como parte de la unidad de pruebas justo antes del "check in"

Respuesta

7

No, no es posible enganchar en t Compila la asamblea y verifica si existe.

Sin embargo, se puede conectar al proceso de compilación, que se compone de algo más que ejecutar el compilador. Puede crear una tarea personalizada de MSBUILD (o NAnt, si está utilizando eso) que verifica el ensamblaje mediante la reflexión después de que se construye y luego falla la compilación si no tiene los atributos necesarios.

Por supuesto, usted debe probablemente todavía verificar esto en el código también. Lo que intenta hacer no es un buen sustituto para un control de tiempo de ejecución adecuado.

+0

Thaks para responder! ¿Qué quiere decir más específicamente con "Lo que intenta hacer no es un buen sustituto para un control de tiempo de ejecución adecuado"? –

+0

@YordanGeorgiev: incluso si tiene un proceso de compilación que asegura que se aplica el atributo, TODAVÍA debe verificar su código de tiempo de ejecución para asegurarse de que se aplica el atributo. No puede dejar de verificar allí porque cree que lo atrapó en tiempo de compilación. – casperOne

+0

Por lo tanto, el requisito para obtener al menos 4 atributos "must have" afectará el rendimiento, debido a la reflexión ... Parece que realizar las comprobaciones en las pruebas unitarias será una mejor idea. –

1

Los atributos son solo de tiempo de ejecución. Sin embargo:

Sería posible crear una regla en FXCop (análisis estático) que fallará si el atributo no está definido, y su proceso de compilación/verificación podría verificar esa regla y fallar adecuadamente.

+0

No es 100% cierto. "Obsoleto" y "Condicional" son ambos atributos de tiempo de compilación. –

1

No conozco ninguna forma de conectarme al proceso de compilación de C#, pero puede adoptar un enfoque diferente y crear una herramienta personalizada iniciada en el evento de compilación posterior que pueda cargar su ensamblaje y lo refleje. Dependiendo de lo que devuelva la herramienta, todo el proceso de compilación resultará en un éxito o una falla, por lo que puede devolver un error con su herramienta y hacer que la compilación falle, a la vez que proporciona más detalles sobre la falla al escribir en la consola.

+0

Agregué un pequeño borrador de este tipo de herramienta debajo de –

4

Puede ejecutar un paso posterior a la compilación que se refleje en la DLL para hacer lo que desee.

Tendrá que escribir una aplicación de línea de comandos que cargue la DLL y refleje los tipos. A continuación, ejecuta esa aplicación de línea de comandos como un paso posterior a la compilación. He hecho esto en el pasado. No es terriblemente difícil de hacer, suponiendo que comprenda la API de reflexión.

PostSharp hace esto para lograr una programación orientada a aspectos. Muy bueno, en realidad.

+0

Brian, ¡gracias! Parecía prometedor lo verificaría mañana ... Aquí en Finlandia son las 10:43 pm ahora. Si descubro algo significativo, publicaré el código ... –

+0

Arggh ... huele que se convertirá en solución cerrada ... –

1

Para mí esto parece más un problema de prueba que un problema de compilación. Es decir, usted está preguntando "¿cómo sé que mi código está escrito correctamente?" donde "escrito correctamente" tiene (entre otras cosas) la connotación de que todas las clases están decoradas con un atributo particular. Consideraría escribir pruebas unitarias que verifiquen que, de hecho, se sigan sus reglas de inclusión de atributos. Puede hacer que su proceso de compilación (y/o registro) ejecute este conjunto particular de pruebas después de la compilación (antes del checkin) como condición para una compilación exitosa (checkin). No romperá la compilación, ya que debe completarse para que las pruebas se ejecuten, pero romperá la compilación, por así decirlo.

+0

Mi temor es que las pruebas de unidad no siempre sean 100% adecuadas para toda la construcción. He visto también hacer trampa en pruebas unitarias hasta cierto punto ... En el contexto de la pregunta si todo el pensamiento ni siquiera compila y los atributos personalizados de forzado son mínimos (por ejemplo, Autor, Versión) –

+0

Escribo probablemente un Prueba de unidad individual por atributo. Pasaría por todas las clases del ensamblaje (o métodos en clases) y verificaría la existencia del atributo. No tendrías que escribir una prueba por separado para cada clase/método. – tvanfosson

+0

Gracias por responder tvanfosson! Usted acertó al señalar que esta verificación debería realizarse como parte del proceso de prueba de la unidad Agregué un fragmento de código de un ejemplo de cómo se podría hacer (aunque no como parte de una prueba de unidad particular, ya que se podría usar NUnit , PEX o lo que sea y no conozco todas las variaciones ... Tu respuesta me recordó que siempre hay que mirar primero la imagen grande y luego comenzar a codificar ... –

0
//PLEASE COMMENT IF YOU FIND BUGS OR SUGGEST IMPROVEMENTS 


using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 

namespace MustHaveAttributes 
{ 
[AttributeClass ("Yordan Georgiev", "1.0.0")] 
class Program 
{ 


static void Main (string [] args) 
{ 
    bool flagFoundCustomAttrOfTypeAttributeClass = false; 
    Console.WriteLine (" START "); 

    // what is in the assembly 
    Assembly a = Assembly.Load ("MustHaveAttributes"); 
    Type[] types = a.GetTypes(); 
    foreach (Type t in types) 
    { 
    object[] arrCustomAttributes = t.GetCustomAttributes (true); 


    if (arrCustomAttributes == null || arrCustomAttributes.GetLength (0) == 0) 
    { 
    //DO NOT CHECK IN 
    ExitProgram (t, "Found class without CustomAttributes"); 
    } 


    foreach (object objCustomAttribute in arrCustomAttributes) 
    { 
    Console.WriteLine ("CustomAttribute for type is {0}", t); 
    if (objCustomAttribute is AttributeClass) 
    flagFoundCustomAttrOfTypeAttributeClass = true; 
    } 

    if (flagFoundCustomAttrOfTypeAttributeClass == false) 
    { //DO NOT CHECK IN 
    ExitProgram (t, "Did not found custom attribute of type AttributeClass"); 
    } 
    Console.WriteLine ("Type is {0}", t); 
    } 
    Console.WriteLine ("{0} types found", types.Length); 

    //NOW REQUIREMENTS IS PASSED CHECK IN 
    Console.WriteLine (" HIT A KEY TO EXIT "); 
    Console.ReadLine(); 
    Console.WriteLine (" END "); 
} 



static void ExitProgram (Type t, string strExitMsg ) 
{ 

    Console.WriteLine (strExitMsg); 
    Console.WriteLine ("Type is {0}", t); 
    Console.WriteLine (" HIT A KEY TO EXIT "); 
    Console.ReadLine(); 

    System.Environment.Exit (1); 

} 
} //eof Program 


//This will fail even to compile since the constructor requires two params 
//[AttributeClass("OnlyAuthor")] 
//class ClassOne 
//{ 

//} //eof class 


////this will not check in since this class does not have required custom 
////attribute 
//class ClassWithoutAttrbute 
//{ } 



[AttributeClass("another author name " , "another version")] 
class ClassTwo 
{ 

} //eof class 


[System.AttributeUsage (System.AttributeTargets.Class | 
System.AttributeTargets.Struct, AllowMultiple = true)] 
public class AttributeClass : System.Attribute 
{ 

public string MustHaveDescription { get; set; } 
public string MusHaveVersion { get; set; } 


public AttributeClass (string mustHaveDescription, string mustHaveVersion) 
{ 
    MustHaveDescription = mustHaveDescription; 
    MusHaveVersion = mustHaveVersion; 
} 

} //eof class 

} // EF espacio de nombres

Cuestiones relacionadas