Можно ли запрашивать пользовательские атрибуты в C # во время компиляции (не во время выполнения) - PullRequest
21 голосов
/ 15 апреля 2009

Другими словами, возможно ли создать сборку, которая даже не компилируется (при условии, что проверочный код не удален), если каждый из классов не имеет («должен иметь») пользовательских атрибутов (например, 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 или другие среды модульного тестирования ...

EDIT В конце ответов, выполняющих проверку, я добавил небольшой фрагмент кода консольной программы ... Не стесняйтесь комментировать, критиковать или предлагать улучшения
Еще раз я понял, что это «требование» должно быть реализовано как часть модульного тестирования непосредственно перед «регистрацией»

Ответы [ 6 ]

7 голосов
/ 15 апреля 2009

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

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

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

4 голосов
/ 15 апреля 2009

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

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

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

1 голос
/ 15 апреля 2009

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

1 голос
/ 15 апреля 2009

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

1 голос
/ 15 апреля 2009

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

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

0 голосов
/ 18 апреля 2009
//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

...