Est-il possible d'interroger des attributs personnalisés en C # au cours de la compilation (non exécution)

StackOverflow https://stackoverflow.com/questions/753255

  •  09-09-2019
  •  | 
  •  

Question

En d'autres termes, pourrait-il être possible de créer l'assemblage, qui ne compile même pas (en supposant que le code de vérification n'est pas supprimé) si chacune des classes n'a pas ( « must have ») des attributs personnalisés (par exemple l'auteur et version)?

Voici le code que je l'ai utilisé pour effectuer des requêtes au cours de l'exécution:

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 { }

//}

Edit: Juste pour justifier mon choix pour la réponse. Je pense que casperOne a fourni la bonne réponse de la question.

Cependant, les raisons de poser la question semblait être faible. Probablement que je devrais commencer à utiliser un outil externe tel que: FinalBuilder ou créer des tests unitaires de vérification pour cette « exigence », en utilisant Pex, Nunit ou d'autres frameworks de tests unitaires ...

EDIT J'ai ajouté un petit extrait de code d'un programme de console à la fin des réponses qui effectue le contrôle ... ne hésitez pas à commenter, critiquer ou suggérer des améliorations
Une fois de plus je réalisais que devrait être mis en œuvre cette « exigence » dans le cadre de l'essai de l'unité juste avant le « check-in »

Était-ce utile?

La solution

Non, il est impossible de brancher dans la compilation de l'ensemble et vérifier si elle existe.

Cependant vous pouvez brancher dans le processus de construction, qui est composée de plus que l'exécution du compilateur. Vous pouvez créer une tâche MSBUILD personnalisée (ou NAnt, si vous utilisez ce) qui vérifie l'assemblage par la réflexion après sa construction, puis l'échec de la construction si elle ne possède pas les attributs requis.

Bien sûr, vous devriez probablement toujours vérifier dans code. Ce que vous essayez de faire est pas un bon substitut pour un contrôle d'exécution approprié.

Autres conseils

Vous pouvez exécuter une étape de post-construction qui reflète sur la DLL de faire ce que vous voulez.

Vous devrez écrire une application en ligne de commande qui charge la DLL et réfléchit sur les types. Vous exécutez alors que l'application de ligne de commande comme une étape de post-construction. Je l'ai fait dans le passé. Il est pas très difficile à faire, en supposant comprendre l'API de réflexion.

PostSharp fait cela pour obtenir la programmation orientée aspect. Plutôt cool, en fait.

Les attributs sont le temps d'exécution uniquement. Cependant:

Il serait possible de créer une règle dans FXCop (analyse statique) qui échouera si l'attribut est pas défini, et votre build / processus checkin pourrait vérifier cette règle et ne parviennent de façon appropriée.

Je ne suis pas au courant d'aucune façon de connecter dans le processus de compilation C #, mais vous pouvez adopter une approche différente et de créer un outil personnalisé lancé sur l'événement post construction qui pourrait charger votre montage et reflète sur ce point. En fonction de ce que l'outil retourne tout le processus de construction se traduira par un succès ou un échec, vous pouvez juste retourner une erreur avec votre outil et faire de la construction échouer, tout en fournissant plus de détails sur l'écriture d'échec à la console.

Pour moi, cela ressemble plus à un problème de test d'un problème de compilation. Autrement dit, vous demandez « comment puis-je savoir que mon code est correctement écrit? » où « correctement écrit » a (entre autres) la connotation que toutes les classes sont décorées avec un attribut particulier. Je considérerais l'écriture des tests unitaires qui permettent de vérifier que vos règles d'inclusion d'attributs sont, en fait, suivi. Vous pourriez avoir votre build (et / ou checkin) processus exécuter cet ensemble particulier de tests après la construction (avant le checkin) comme condition d'une construction réussie (checkin). Il ne cassera pas la compilation, car cela doit remplir pour que les tests à exécuter, mais il va briser la construction, pour ainsi dire.

//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 

} // EOF espace de noms

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top