2009-04-15 6 views
17

Nói cách khác có thể tạo assembly, mà thậm chí không biên dịch (giả sử mã kiểm tra không được gỡ bỏ) nếu mỗi các lớp không có thuộc tính tùy chỉnh ("phải có") (ví dụ: Tác giả và Phiên bản)?Có thể truy vấn thuộc tính tùy chỉnh trong C# trong thời gian biên dịch (không phải thời gian chạy)

Đây là mã tôi đã sử dụng để truy vấn trong thời gian chạy:

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 { } 

//} 

Chỉnh sửa: Chỉ cần để biện minh cho sự lựa chọn của tôi cho câu trả lời. Tôi nghĩ casperOne đã cung cấp câu trả lời đúng cho câu hỏi.

Tuy nhiên, lý do đặt câu hỏi dường như là weak. Có lẽ tôi nên bắt đầu sử dụng một số công cụ bên ngoài như: FinalBuilder hoặc tạo kiểm tra đơn vị kiểm tra cho "yêu cầu" này, sử dụng Pex, Nunit hoặc các khuôn khổ kiểm tra đơn vị khác ...

EDIT Tôi đã thêm một nhỏ code snippet của chương trình điều khiển ở cuối câu trả lời thực hiện kiểm tra ... cảm thấy tự do để bình luận, phê bình hoặc đề xuất cải tiến
Một lần nữa tôi nhận ra rằng "yêu cầu" này cần được thực hiện như một phần của thử nghiệm đơn vị ngay trước "check in"

Trả lời

7

Không, không thể móc vào t ông biên soạn hội đồng và kiểm tra xem nó có tồn tại hay không.

Tuy nhiên bạn có thể móc vào quá trình tạo, được tạo thành từ nhiều hơn là chỉ chạy trình biên dịch. Bạn có thể tạo một tác vụ MSBUILD tùy chỉnh (hoặc NAnt, nếu bạn đang sử dụng nó) để kiểm tra assembly thông qua sự phản chiếu sau khi nó được xây dựng và sau đó không thành công nếu nó không có các thuộc tính bắt buộc.

Tất nhiên, bạn có lẽ cũng nên vẫn xác minh mã này bằng mã. Những gì bạn đang cố gắng làm không phải là một thay thế tốt cho một kiểm tra thời gian chạy thích hợp.

+0

Thaks cho trả lời! Bạn có ý nghĩa cụ thể hơn bằng cách "Những gì bạn đang cố gắng làm không phải là một thay thế tốt cho một kiểm tra thời gian chạy thích hợp."? –

+0

@YordanGeorgiev: Ngay cả khi bạn có quy trình xây dựng để đảm bảo thuộc tính được áp dụng, bạn nên STILL kiểm tra trong mã thời gian chạy của bạn để đảm bảo rằng thuộc tính được áp dụng. Bạn không thể ngừng kiểm tra ở đó bởi vì bạn nghĩ rằng bạn bắt gặp nó lúc biên dịch. – casperOne

+0

Vì vậy, yêu cầu nhận được ít nhất 4 "phải có" thuộc tính sẽ ảnh hưởng đến hiệu suất, vì phản ánh ... Dường như việc thực hiện kiểm tra trong bài kiểm tra Đơn vị sẽ là ý tưởng tốt hơn?! –

1

Thuộc tính chỉ chạy trong thời gian. Tuy nhiên:

Có thể tạo quy tắc trong FXCop (phân tích tĩnh) sẽ không thành công nếu thuộc tính không được xác định và quy trình xây dựng/kiểm tra của bạn có thể kiểm tra quy tắc đó và không phù hợp.

+0

Không đúng 100%. "Lỗi thời" và "Có điều kiện" đều là các thuộc tính thời gian biên dịch. –

1

Tôi không biết cách móc vào quy trình biên dịch C#, nhưng bạn có thể thực hiện một cách tiếp cận khác và tạo công cụ tùy chỉnh được khởi chạy trên sự kiện xây dựng bài đăng. Tùy thuộc vào công cụ trả về toàn bộ quá trình xây dựng sẽ dẫn đến thành công hay thất bại, vì vậy bạn chỉ có thể trả về lỗi với công cụ của mình và tạo bản dựng không thành công, đồng thời cung cấp thêm chi tiết về lỗi ghi vào bàn điều khiển.

+0

Tôi đã thêm một bản nháp nhỏ của loại công cụ này dưới đây –

4

Bạn có thể chạy một bước sau xây dựng phản ánh trên DLL để làm những gì bạn muốn.

Bạn sẽ phải viết một ứng dụng dòng lệnh tải tệp DLL và phản ánh trên các loại. Sau đó, bạn chạy ứng dụng dòng lệnh đó dưới dạng bước đăng bài. Tôi đã làm điều này trong quá khứ. Nó không phải là khó khăn để làm, giả sử bạn hiểu các API phản ánh.

PostSharp thực hiện việc này để đạt được lập trình hướng khía cạnh. Khá thú vị, thực sự.

+0

Brian, cảm ơn! Nhìn đầy hứa hẹn, tôi sẽ kiểm tra nó vào ngày mai ... Tại Phần Lan bây giờ là 10:43 chiều. Nếu tôi tìm ra điều gì đó có ý nghĩa, tôi sẽ đăng mã ... –

+0

Arggh ... có mùi sẽ được chuyển thành giải pháp gần ... –

1

Với tôi điều này có vẻ giống như một vấn đề thử nghiệm hơn là một vấn đề biên dịch. Tức là, bạn đang hỏi "làm cách nào để biết mã của tôi được viết chính xác?" nơi "viết chính xác" có (trong số những thứ khác) ý nghĩa rằng tất cả các lớp được trang trí với một thuộc tính cụ thể. Tôi sẽ xem xét việc viết các bài kiểm tra đơn vị xác minh rằng các quy tắc bao gồm thuộc tính của bạn, trên thực tế, theo sau. Bạn có thể có quá trình xây dựng (và/hoặc checkin) của bạn chạy bộ kiểm tra cụ thể này sau khi xây dựng (trước khi checkin) như là một điều kiện của một xây dựng thành công (checkin). Nó sẽ không phá vỡ biên dịch, vì điều đó cần phải hoàn thành để các bài kiểm tra chạy, nhưng nó sẽ phá vỡ bản dựng, để nói.

+0

Nỗi sợ của tôi là bài kiểm tra Đơn vị không phải lúc nào cũng phù hợp 100% cho toàn bộ bản dựng. Tôi đã thấy cũng gian lận về kiểm tra đơn vị ở một mức độ nào đó ... Trong ngữ cảnh của câu hỏi nếu toàn bộ suy nghĩ thậm chí sẽ không biên dịch và các thuộc tính tùy chỉnh bắt buộc là tối thiểu (ví dụ: Tác giả, Phiên bản) –

+0

Tôi có thể viết kiểm tra đơn vị duy nhất cho mỗi thuộc tính. Nó sẽ đi qua tất cả các lớp trong assembly (hoặc các phương thức trong các lớp) và xác minh sự tồn tại của thuộc tính. Bạn sẽ không phải viết một bài kiểm tra riêng biệt cho mỗi lớp/phương pháp. – tvanfosson

+0

Cảm ơn bạn đã trả lời tvanfosson! Bạn xác định rằng kiểm tra này nên được thực hiện như là một phần của quá trình thử nghiệm đơn vị Tôi đã thêm đoạn mã ví dụ về cách thực hiện (tuy nhiên không phải là một phần của thử nghiệm đơn vị cụ thể vì người dùng có thể sử dụng NUnit , PEX hoặc bất cứ điều gì khác và tôi không biết tất cả các biến thể ... Câu trả lời của bạn nhắc nhở tôi rằng người ta nên luôn luôn nhìn đầu tiên ở bức tranh lớn và sau đó bắt đầu viết mã ... –

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