是否可以在编译时(而不是运行时)在C#中查询自定义属性
换句话说,如果每个类都没有(“必须有”)自定义属性(例如Author和Version),是否可以创建甚至不编译的程序集(假设没有删除检查代码) 以下是我在运行时用于查询的代码:是否可以在编译时(而不是运行时)在C#中查询自定义属性,c#,custom-attributes,C#,Custom Attributes,换句话说,如果每个类都没有(“必须有”)自定义属性(例如Author和Version),是否可以创建甚至不编译的程序集(假设没有删除检查代码) 以下是我在运行时用于查询的代码: using System; using System.Reflection; using System.Collections.Generic; namespace ForceMetaAttributes { [System.AttributeUsage ( System.AttributeTargets
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 { }
//}
编辑:只是为了证明我对答案的选择是正确的。
我认为卡斯佩林提供了这个问题的正确答案
然而,提出这个问题的原因似乎是。也许我应该开始使用一些外部工具,例如:
或者使用Pex、Nunit或其他单元测试框架创建单元测试来检查此“需求”
编辑
我在答案的末尾添加了一个小的控制台程序来执行检查。。。请随意评论、批评或建议改进我再一次意识到,这个“需求”应该在“检入”之前作为单元测试的一部分来实现。不,不可能连接到汇编并检查它是否存在 但是,您可以钩住构建过程,构建过程不仅仅是运行编译器。您可以创建一个自定义MSBUILD任务(或NAnt,如果您正在使用该任务),该任务在生成程序集后通过反射检查该程序集,如果该程序集不具有所需的属性,则生成失败
当然,您可能还应该在代码中验证这一点。您试图做的并不能很好地替代正确的运行时检查。属性仅用于运行时。然而:
在FXCop(静态分析)中创建一个规则是可能的,如果属性未定义,该规则将失败,您的构建/签入过程可能会检查该规则并相应地失败。我不知道有什么方法可以连接到C#编译过程,但是,您可以采用不同的方法,创建在生成后事件上启动的自定义工具,该工具可以加载您的程序集并对此进行反思。根据工具返回的内容,整个生成过程将导致成功或失败,因此您可能只需使用工具返回错误并使生成失败,同时提供有关写入控制台失败的更多详细信息。您可以运行生成后步骤,该步骤反映在DLL上,以执行所需操作 您必须编写一个命令行应用程序来加载DLL并反映类型。然后将该命令行应用程序作为生成后步骤运行。我过去也这样做过。假设您理解反射API,那么这并不十分困难
这样做是为了实现面向方面的编程。实际上相当酷。对我来说,这更像是一个测试问题,而不是编译问题。也就是说,您会问“我如何知道我的代码编写正确?”其中“编写正确”的含义是(除其他外)所有类都用特定属性修饰。我会考虑编写单元测试,以验证您的属性包含规则实际上是遵循的。您可以让构建(和/或签入)过程在构建之后(签入之前)运行这组特定的测试,作为成功构建(签入)的条件。它不会破坏编译,因为这需要完成才能运行测试,但可以说它会破坏构建
//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名称空间现在,通过编写Roslyn分析器,这完全可以实现。您可以使用语法树或语义模型。
(建议使用后者,因为属性名称的引用方式很复杂,例如使用
使用别名)。不是100%正确。“过时”和“有条件”都是编译时属性。我担心的是,单元测试并不总是100%适合整个构建。在某种程度上,我也看到过单元测试作弊。。。在这个问题的上下文中,如果整个think甚至不会编译,并且强制自定义属性是最小的(例如Author,Version),我可能会为每个属性编写一个单元测试。它将遍历程序集中的所有类(或类中的方法),并验证属性的存在。您不必为每个类/方法编写单独的测试。谢谢您的回答!您应该指出这个检查应该作为单元测试过程的一部分来执行,这是正确的。我添加了一个示例的代码片段,介绍了如何执行该检查(然而,这不是特定单元测试的一部分,因为人们可以使用NUnit、PEX或其他任何东西,我不知道所有的变化……你的回答提醒我,人们应该先看大局,然后再开始编码……Thaks用于回答!你更具体地说是什么意思“您试图做的并不能很好地替代正确的运行时检查。“?@YordanGeorgiev:即使您有一个生成过程来确保应用了该属性,您仍然应该检查运行时代码以确保应用了该属性。您不能停止检查,因为您认为您在编译时捕获了该属性。因此,必须满足至少获取4”的要求”属性会影响性能,因为反射…似乎在单元测试中执行检查比这个主意更好?!困扰我的还有,MSBUILD并不适用于Web项目…而且NAnt似乎太重了,无法满足这样一个“简单”的要求(或者可能是“简单”是因为我缺乏经验…)@卡斯佩林:我还是不明白为什么你认为运行时检查是必要的,至少如果你的程序集有强名称的话。布莱恩,谢谢!看起来很有希望我明天会检查它……芬兰现在是晚上10:43。如果我找到有意义的东西,我会发布代码……我在下面添加了这种类型的工具的一个小草稿