2010-01-30 53 views
8

如果滿足某些條件,是否可以在C#中編寫將向代碼添加預處理器指令的MSIL代碼,例如#warning?或者,也許這可以通過反思來完成,我不知道。C#:寫入MSIL以添加預處理器指令

我想寫一個自定義屬性,如果錯誤地應用到類的方法或屬性,將生成編譯器警告。使用現有的Obsolete屬性將不起作用,因爲只使用我的自定義屬性會導致警告,而我不想那樣做。我希望自定義屬性構造函數檢查一個條件,如果該條件爲真,則會導致編譯警告。

更新:在回顧了我的問題之後,我想我所要求的是不可能的,因爲我混合了編譯時和運行時約束。我想我最終會用一個後期構建任務來檢查剛構建的DLL,並在條件爲真時讓它吐出錯誤消息。

+1

我想我明白你在說什麼,你想要一個自定義屬性應用於一個方法,併發出一個#warning指令,如果條件滿足..我不認爲你可以這樣做,因爲#warning是一個編譯時間指令.. – t0mm13b 2010-01-30 20:02:43

+0

的方式很好的問題... +1從我... – t0mm13b 2010-01-30 20:05:52

回答

6

我看到這個問題來自您的前一個線程。錯誤地引用偉大的傑米Zawinski:「有些人遇到問題時,認爲」我知道,我會用屬性。「現在他們有兩個問題」。

一個屬性僅僅是帶外數據,被編譯到程序集的元數據中。它不會影響程序執行或工具行爲,除非程序或工具被明確編程爲識別特定屬性。它需要使用反射。

你需要做的是編寫你自己的工具。它應該在構建程序集後執行,並使用項目的構建後步驟。它需要加載程序集並使用Reflection來迭代程序集中的類型。對於每種類型,使用Type.GetMethods()迭代方法並使用MethodInfo.GetCustomAttributes()來發現和構造可能已編程的屬性。

您可以使用Type.GetInterfaces()來發現哪些接口是由類型實現的。您現在可以在發現實現接口方法但缺少一個表示如此的屬性的方法時抱怨。而你的最終目標:當你看到一個屬性表明它實現了一個接口方法但是類型不再繼承它的方法時,你可以抱怨。

如果您看到任何令人反感的內容,請使用Environment.ExitCode使工具無法正常工作。這需要執法。順便說一句:程序員真的很討厭打破構建。這可能會鼓勵他們在宗教上使用這個屬性。或者它可能會鼓勵他們編輯構建後步驟。

+0

優秀的答案,而且我會最終跟隨的路線。我不知道我是否應該選擇你作爲選擇回答這個問題,因爲它沒有直接回答我的問題,MSIL,但你解決後面幾個我最近的問題的問題! – 2010-01-30 21:00:01

+2

你是說他應該使用正則表達式嗎? ;) – 2010-01-30 21:44:07

+0

@Remus - 聽起來就像你所知道的引用。莎拉可能是他/她。不,正則表達式不在這裏。 – 2010-01-30 21:48:58

1

你可以用post-build task這樣做來反映你編譯的代碼並檢查條件。我沒有任何創建MSBuild任務的經驗,但這是一個可以開始的地方。

.NET 4.0發佈後的另一個建議是使用Code Contracts來指定對屬性構造函數的參數的需求。這將在編譯時被捕獲。

2

編譯器存儲兩個東西自定義屬性:

  • 屬性構造函數調用
  • 的數據,每一個參數傳遞給構造

構造函數只調用的時候,應用程序正在運行,某人爲您的Assembyl,Type,MethodInfo,ParameterInfo等調用GetCustomAttributes。

您有s青梅其它問題需要考慮:

  • 編寫編譯階段後運行一個自定義的MSBuild任務,加載編譯的程序集,並檢查應用程序的屬性使用。
  • 使用AttributeUsage屬性指定可以應用該屬性的代碼項。
  • 將屬性驗證推遲到運行時。
0

我懷疑答案是否定的,你不能,因爲#warning指令是一個CSC的東西(即你指導編譯器以某種方式行事)。當編寫原始MISL時,顯然,CSC不會混合,因此沒有編譯器可以直接執行某些操作。

基本上一個指令(指示相似「#warning後」是CSC指令以某種方式表現規定的條件下。

2

總之,沒有。預處理指令沒有IL表示,因爲它們只在編譯源文件時使用的元數據存在。

您正在做的事情可能會更好,如custom FxCop rule

1

我的直覺是沒有你不能注入一個基於自定義屬性和條件的#warning指令,因爲它在編譯時被編譯器捕獲,它就像一個雞和雞蛋的情況,作爲自定義屬性必須在注入#warning之前先進行評估,但爲了實現該目的,編譯時必須首先執行。

希望這會有所幫助, 最好的問候, 湯姆。

2

你有沒有聽說過Boo?它有一些有趣的方法可以讓你連接到編譯器管道。一種這樣的特性叫做syntactic attributes,它們是實現編譯器調用的接口的屬性,以便它們可以參與代碼生成。

class Person: 
    [getter(FirstName)] 
    _fname as string 

    [getter(LastName)] 
    _lname as string 

    def constructor([required] fname, [required] lname): 
    _fname = fname 
    _lname = lname 

此代碼中的屬性將爲字段生成公共getter,併爲構造函數參數生成null檢查。它將在編譯後的程序集中結束。

I've always wanted這種可擴展性成爲C#編譯器的一部分。也許它會有一天。在此之前,您可以使用後編譯器,如CciSharp。 CCiSharp將根據程序集中的特殊屬性重寫CIL,就像使用Boo synctatic屬性一樣。

鑑於此代碼:

class Foo { 
    [Lazy] 
    public int Value { 
    get { return Environment.Ticks; } 
    } 
} 

CCiSharp將根據LazyAttribute代碼變異成這樣:

class Foo { 
    int Value$Value; // compiler generated 
    int Value$Initialized; 
    int GetValueUncached() { 
    return Environment.Ticks; 
    } 
    public int Value { 
    get { 
     if(!this.Value$Initialized) { 
     this.Value$Value = this.GetValueUncached(); 
     this.Value$Initialized = true; 
     } 
     return this.Value$Value; 
    } 
} 

CCiSharp是基於Common Compiler Infrastructure項目,用於實現code contracts後相同編譯器在即將到來的.NET Framework 4.0中。

所以這就是你如何改變生成的CIL。

但是,#warning指令沒有CIL表示,它只是一個編譯器指令。要添加此指令,必須進行變異的不是生成的CIL,而是C#代碼本身。你必須爲此實現一個C#解析器。我認爲最好的辦法是,在其他應答指出,創建一個生成後事件,這將反映在生成的程序集,併發出警告所需。