É possível consultar atributos personalizados em C # durante tempo de compilação (em tempo de execução não)

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

  •  09-09-2019
  •  | 
  •  

Pergunta

Em outras palavras poderia ser possível criar a montagem, que nem sequer compilar (assumindo que o código de verificação não é removido) se cada uma das Classes não tem ( "must have") atributos personalizados (por exemplo Autor e Version)?

Aqui está o código que tenho usado para consulta durante o tempo de execução:

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: Só para justificar a minha escolha para a resposta. Acho casperOne deu a resposta correta da questão.

No entanto as razões para fazer a pergunta parecia ser fraco em tempo de compilação-tim não-gerência. Provavelmente eu deveria começar a usar alguma ferramenta externa, como: FinalBuilder ou criar testes de unidade verificando para este "exigência", usando Pex, Nunit ou outras estruturas de teste de unidade ...

Editar Eu adicionei um pequeno fragmento de código de um programa de console, no final das respostas que realiza a verificação ... sinta-se livre para comentar, criticar ou sugerir melhorias
Mais uma vez eu percebi que essa "exigência" deve ser implementado como parte da unidade de teste imediatamente antes do "check-in"

Foi útil?

Solução

Não, não é possível ligar para a compilação do conjunto e verificar se ele existe.

No entanto , você pode ligar para o processo de construção, que é composta de mais do que apenas executar o compilador. Você poderia criar uma tarefa MSBUILD personalizada (ou NAnt, se você estiver usando isso) que verifica a montagem através da reflexão depois de ser construído e, em seguida, deixar a compilação se ele não tem os atributos necessários.

É claro, você provavelmente deve ainda verificar isso no código também. O que você está tentando fazer não é um bom substituto para uma verificação de tempo de execução adequada.

Outras dicas

Você pode executar uma etapa de pós-construção que reflete sobre a DLL para fazer o que quiser.

Você terá que escrever um aplicativo de linha de comando que carrega a DLL e reflete sobre os tipos. Em seguida, executar esse aplicativo de linha de comando como uma etapa pós-build. Eu tenho feito isso no passado. Não é terrivelmente difícil de fazer, supondo que você entender a API reflexão.

PostSharp faz isso para conseguir a programação orientada a aspectos. Muito legal, na verdade.

Os atributos são executados somente tempo. No entanto:

Seria possível criar uma regra no FXCop (análise estática), que falhará se o atributo não está definido, e seu / coleira processo de construção poderia verificar essa regra e falhar de forma adequada.

Eu não estou ciente de qualquer maneira de ligar para o processo de compilação C #, mas você pode ter uma abordagem diferente e criar uma ferramenta personalizada lançado no evento pós construir o que poderia carregar seu conjunto e reflete sobre isso. Dependendo do que a ferramenta retorna todo o processo de construção irá resultar em um sucesso ou um fracasso, então você pode apenas retornar um erro com a sua ferramenta e fazer a compilação falhar, ao fornecer mais detalhes sobre a falha de escrita para console.

Para mim, isso parece mais como um problema de testes do que um problema de compilação. Ou seja, você está perguntando "como eu sei que meu código está escrito corretamente?" onde "escritas corretamente" tem (entre outras coisas) a conotação que todas as classes são decorados com um determinado atributo. Eu consideraria escrever testes de unidade que verificam que as regras de inclusão atributo são, na verdade, seguido. Você poderia ter a sua construção (e / ou check-in) Processo de executar este conjunto específico de testes após a construção (antes do check-in), como condição de uma compilação bem-sucedida (check-in). Não vai quebrar a compilação, uma vez que as necessidades para completar a fim de que os testes a serem executados, mas vai quebrar a construir, por assim dizer.

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top