24

换句话说,是否有可能创建程序集,如果每个类都没有(“必须”)自定义属性(例如 Author 和 Version),它甚至不会编译(假设检查代码没有被删除)?

这是我在运行时用于查询的代码:

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 I added a small code snippet of a console program at the end of the answers that performs the check ... feel free to comment, criticize or suggest improvements
Once more I realized that this "requirement" should be implemented as part of the unit testing just before the "check in"

4

7 回答 7

9

No, it is not possible to hook into the compilation of the assembly and check if it exists.

However you can hook into the build process, which is made up of more than just running the compiler. You could create a custom MSBUILD task (or NAnt, if you are using that) which checks the assembly through reflection after it is built and then fail the build if it doesn't have the required attributes.

Of course, you should probably still verify this in code as well. What you are trying to do is not a good substitute for a proper runtime check.

于 2009-04-15T19:12:58.023 回答
4

You can run a post-build step that reflects on the DLL to do what you want.

You will have to write a command-line app that loads the DLL and reflects on the types. You then run that command-line app as a post-build step. I have done this in the past. It is not terribly difficult to do, assuming you understand the reflection API.

PostSharp does this to achieve aspect oriented programming. Pretty cool, actually.

于 2009-04-15T19:17:03.850 回答
1

Attributes are run time only. However :

It would be possible to create a rule in FXCop (static analysis) that will fail if the attribute is not defined, and your build/checkin process could check that rule and fail appropriately.

于 2009-04-15T19:13:56.370 回答
1

I'm not aware of any way to hook into the C# compilation process, but you may take a different approach and create a custom tool launched on the post build event which could load your assembly and reflects on that. Depending on what the tool returns the whole build process will result in a success or a failure, so you may just return an error with your tool and make the build fail, while providing more details about the failure writing to console.

于 2009-04-15T19:14:09.393 回答
1

To me this seems more like a testing problem than a compilation problem. That is, you're asking "how do I know that my code is written correctly?" where "written correctly" has (among other things) the connotation that all classes are decorated with a particular attribute. I would consider writing unit tests that verify that your attribute inclusion rules are, in fact, followed. You could have your build (and/or checkin) process run this particular set of tests after the build (before the checkin) as a condition of a successful build (checkin). It won't break the compile, since that needs to complete in order for the tests to run, but it will break the build, so to speak.

于 2009-04-15T19:22:56.673 回答
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 

} //eof namespace

于 2009-04-18T06:16:03.903 回答
0

This is now fully possible using by writing a Roslyn analyzer. You could use either the syntax tree or the semantic model. (The latter is recommended, due to the complexities of how attribute names can be referenced, for example using using aliasing).

于 2021-05-02T13:52:10.820 回答