是否可以在编译时(而不是运行时)查询 C# 中的自定义属性
-
09-09-2019 - |
题
换句话说,如果每个类没有(“必须有”)自定义属性(例如作者和版本),是否可以创建甚至无法编译的程序集(假设未删除检查代码)?
这是我在运行时用于查询的代码:
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 提供了问题的正确答案。
然而提出这个问题的原因似乎是 虚弱的. 。也许我应该开始使用一些外部工具,例如:最终生成器或使用 Pex 、 Nunit 或其他单元测试框架创建单元测试来检查此“要求”...
编辑我加了一个小 代码片段 执行检查的答案末尾的控制台程序...请随意发表评论、批评或提出改进建议
我再次意识到这个“要求”应该在“签入”之前作为单元测试的一部分来实现
解决方案
没有,这是不可能挂接到组件的汇编和检查它是否存在。
然而你可以挂接到生成过程中,它是由不只是运行编译器更的。您可以创建一个自定义的MSBuild任务(或恶性,如果您正在使用),它通过反射检查装配它建成后,然后构建失败,如果它不具备所需的属性。
当然,你或许应该的还是的代码验证这一点。你所要做的是不是一个适当的运行时检查一个很好的替代品。
其他提示
您可以运行一个生成后的步骤,反映了DLL做你想做什么。
您必须编写加载DLL和类型反映了一个命令行应用程序。然后运行该命令行应用程序的生成后步骤。我在过去做到了这一点。它是不是非常困难的事,假设你明白反射API。
PostSharp 这样做是为了实现面向方面的编程。很酷,实际上。
属性仅运行时间。但是:
这将有可能创造的FxCop(静态分析)的规则,如果没有定义的属性,将失败,并且您的构建/签到过程可以检查该规则并不能恰如其分。
我不知道的任何方式挂接到C#编译过程,但你可以采取不同的方法,并创建上可以加载程序集和反映后生成事件展开了自定义工具。根据什么工具返回整个构建过程将导致成功或失败,那么你可能只返回一个错误与您的工具,使构建失败,同时提供关于失效写入到控制台的更多细节。
对我来说这似乎更像是一个测试问题比汇编问题。也就是说,你问“我怎么知道,我的代码编写正确?”其中,“正确地写入”有(其中包括)的内涵,所有的类都具有特定属性的装饰。我会考虑编写单元测试,验证您的属性包含规则,事实上,紧随其后。你可以有你的构建(和/或签)的进程中运行版本(签入前)后,该组特定的测试作为一个成功的构建(签)的条件。它不会打破编译,因为需要为了完成运行测试,但它会打破建立,可以这么说。
//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命名空间