2009-04-15 1 views
17

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

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

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: Просто чтобы оправдать свой выбор для ответа. Я думаю, что casperOne предоставил правильный ответ на вопрос.

Однако причины для запроса вопроса были weak. Возможно, я должен начать использовать некоторые внешние средства, такие как: FinalBuilder или создавать модульные тесты, проверяя для этого «требования», используя PEX, NUnit или другие структуры модульного тестирования ...

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

ответ

7

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

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

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

+0

Thaks для ответа на! Что вы имеете в виду более конкретно: «То, что вы пытаетесь сделать, не является хорошей заменой для правильной проверки времени выполнения»? –

+0

@YordanGeorgiev: Даже если у вас есть процесс сборки, который гарантирует, что атрибут применяется, вы должны STILL проверить свой код во время выполнения, чтобы убедиться, что этот атрибут применяется. Вы не можете остановить проверку там, потому что думаете, что поймали ее во время компиляции. – casperOne

+0

Таким образом, требование получения атрибутов не менее 4 "должно иметь" повлияет на производительность, из-за отражения ... Кажется, что выполнение проверок в модульных тестах будет чем лучшая идея?! –

1

Атрибуты - это время выполнения. Тем не менее:

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

+0

Не верно 100%. «Устаревшие» и «Условные» являются атрибутами времени компиляции. –

1

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

+0

Я добавил небольшую черноту этого инструмента для резки –

4

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

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

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

+0

Брайан, спасибо! Посмотрел перспективно, я проверю это завтра ... Здесь в Финляндии сейчас 10:43. Если я выясню что-то значимое, я отправлю код ... –

+0

Arggh ... запахи, что он будет преобразован в закрытое решение ... –

1

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

+0

Мой страх в том, что модульные тесты не всегда на 100% подходят для всей сборки. Я видел в некоторых случаях обманывание на модульных тестах ... В контексте вопроса, если бы все мысли даже не компилировались, а принудительные пользовательские атрибуты минимальны (например, автор, версия) –

+0

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

+0

Спасибо за ответ tvanfosson! Вы, где правильно указать, что эта проверка должна выполняться как часть процесса тестирования устройства Я добавил фрагмент кода примера того, как это можно сделать (но не как часть конкретного модульного теста, так как можно использовать NUnit , PEX или что-то еще, и я не знаю всех вариаций ... Ваш ответ напомнил мне, что всегда нужно смотреть сначала на большую картинку, а затем на начало кодирования ... –

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

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

Смежные вопросы