Подтвердить что ты не робот

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

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

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

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

//}

Изменить: просто чтобы оправдать мой выбор для ответа. Я думаю, что casperOne дал правильный ответ на вопрос.

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

PostSharp делает это для достижения аспектно-ориентированного программирования. На самом деле довольно круто.

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

}//пространство имен