是否可以在编译期间(而不是运行时)在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 { } //} 

编辑:只是为了certificate我的答案选择。 我认为casperOne提供了问题的正确答案。

然而,问这个问题的理由似乎很弱 。 可能我应该开始使用一些外部工具,例如: FinalBuilder或创建unit testing来检查这个“需求”,使用Pex,Nunit或其他unit testing框架……

编辑我在执行检查的答案的末尾添加了一个控制台程序的小代码片段 …随意评论,批评或建议改进
我再次意识到这个“要求”应该在“登记入住”之前作为unit testing的一部分来实现

不,不可能挂钩到程序集的编译并检查它是否存在。

但是,您可以挂钩构建过程,该过程不仅仅是运行编译器。 您可以创建一个自定义的MSBUILD任务(或NAnt,如果您正在使用它),它在构建后通过reflection检查程序集,如果它没有所需的属性,则会使构建失败。

当然,你应该仍然可以在代码中validation这一点。 您要做的不是正确运行时检查的良好替代品。

您可以运行反映DLL的后构建步骤来执行您想要的操作。

您将不得不编写一个加载DLL并反映类型的命令行应用程序。 然后,您将该命令行应用程序作为构建后步骤运行。 我过去做过这个。 假设您了解reflectionAPI,那么这样做并不是非常困难。

PostSharp这样做是为了实现面向方面的编程。 实际上非常酷。

属性仅限运行时。 但是:

可以在FXCop(静态分析)中创建一个规则,如果未定义该属性,该规则将失败,并且您的构建/签入过程可以检查该规则并适当地失败。

我不知道有什么方法可以挂钩C#编译过程,但是你可以采用不同的方法并创建一个在post build事件上启动的自定义工具,它可以加载你的程序集并反映出来。 根据工具返回的内容,整个构建过程将导致成功或失败,因此您可能只是使用工具返回错误并使构建失败,同时提供有关写入控制台的失败的更多详细信息。

对我来说,这似乎更像是一个测试问题,而不是一个编译问题。 也就是说,你问“我怎么知道我的代码写得正确?” 其中“正确书写”具有(除其他外)所有类用特定属性修饰的内涵。 我会考虑编写unit testing来validation您的属性包含规则实际上是遵循的。 您可以让构建(和/或签入)过程在构建之后(签入之前)运行此特定测试集,作为成功构建(签入)的条件。 它不会破坏编译,因为它需要完成才能运行测试,但它会破坏构建,可以这么说。

 //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命名空间