Возможно ли запрашивать пользовательские атрибуты в C # во время компиляции ( не во время выполнения)

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

  •  09-09-2019
  •  | 
  •  

Вопрос

Другими словами, возможно ли создать сборку, которая даже не компилируется (при условии, что проверяющий код не удален ), если каждый из классов не имеет ( "должен иметь" ) пользовательских атрибутов ( например, Author и Version ) ?

Вот код, который я использовал для запроса во время выполнения :

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

//}

Редактировать:Просто чтобы оправдать мой выбор в качестве ответа.Я думаю, что касперОне дал правильный ответ на этот вопрос.

Однако причины для того, чтобы задать этот вопрос, по- видимому, были слабый.Вероятно, мне следует начать использовать какой-нибудь внешний инструмент, такой как :Завершающий конструктор или создайте модульные тесты, проверяющие это "требование", используя Pex , Nunit или другие платформы модульного тестирования ...

Редактировать Я добавил небольшой фрагмент кода консольной программы в конце ответов , которая выполняет проверку ...не стесняйтесь комментировать, критиковать или предлагать улучшения
Еще раз я понял, что это "требование" должно быть реализовано как часть модульного тестирования непосредственно перед "регистрацией".

Это было полезно?

Решение

Нет, подключиться к компиляции сборки и проверить, существует ли она, невозможно.

Однако вы можете подключиться к процессу сборки, который состоит не просто из запуска компилятора.Вы могли бы создать пользовательскую задачу MSBUILD (или NAnt, если вы ее используете), которая проверяет сборку посредством отражения после ее сборки, а затем завершает сборку с ошибкой, если у нее нет требуемых атрибутов.

Конечно, вам, вероятно, следует все еще проверьте это также в коде.То, что вы пытаетесь сделать, не является хорошей заменой надлежащей проверки во время выполнения.

Другие советы

Вы можете запустить этап после сборки, который отражается на DLL, чтобы делать то, что вы хотите.

Вам нужно будет написать приложение командной строки, которое загружает DLL и отражает типы.Затем вы запускаете это приложение командной строки на этапе после сборки.Я делал это в прошлом.Это не так уж сложно сделать, предполагая, что вы понимаете reflection API.

Постшарповый делает это для достижения аспектно-ориентированного программирования.На самом деле, довольно круто.

Атрибуты - это только время выполнения.Однако :

Можно было бы создать правило в FxCop (статический анализ), которое приведет к сбою, если атрибут не определен, и ваш процесс сборки / проверки может проверить это правило и выполнить соответствующий сбой.

Я не знаю ни о каком способе подключения к процессу компиляции C #, но вы можете использовать другой подход и создать пользовательский инструмент, запускаемый при событии post build, который мог бы загрузить вашу сборку и отразить это.В зависимости от того, что возвращает инструмент, весь процесс сборки приведет к успеху или сбою, поэтому вы можете просто вернуть ошибку с помощью своего инструмента и вызвать сбой сборки, при этом предоставив более подробную информацию о сбое при записи в консоль.

Мне это больше похоже на проблему тестирования, чем на проблему компиляции.То есть вы спрашиваете "откуда я знаю, что мой код написан правильно?" где "написано правильно" имеет (среди прочего) значение того, что все классы украшены определенным атрибутом.Я бы подумал о написании модульных тестов, которые проверяют, действительно ли соблюдаются ваши правила включения атрибутов.Вы могли бы заставить свой процесс сборки (и / или проверки) запустить этот конкретный набор тестов после сборки (до проверки) в качестве условия успешной сборки (checkin).Это не нарушит компиляцию, поскольку для запуска тестов ее необходимо завершить, но, так сказать, нарушит сборку.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top