2016-01-18 21 views
0

就拿這個代碼創建一個呼叫被叫AddNumbers方法的自定義委託:從方法體提取代碼並使用表達式內聯執行它?

public static int AddNumbers(int a, int b) 
{ 
    return a + b; 
} 

static void Main(string[] args) 
{ 
    ParameterExpression 
     arga = Expression.Parameter(typeof(int)), 
     argb = Expression.Parameter(typeof(int)); 

    MethodCallExpression result = Expression.Call(typeof(Program_ModelBinding).GetMethod("AddNumbers"), arga, argb); 

    var deli = Expression.Lambda(result, arga, argb).Compile() as Func<int, int, int>; 

    Console.WriteLine(deli(2, 4)); 
} 

編譯爲:

.Lambda #Lambda1<System.Func`3[System.Int32,System.Int32,System.Int32]>(
    System.Int32 $var1, 
    System.Int32 $var2) { 
    .Call ModelBinding.Program_ModelBinding.AddNumbers(
     $var1, 
     $var2) 
} 

然而,爲了減少繁瑣並保持堆棧整齊,我會喜歡能夠內聯AddNumbers方法,以便其方法體在代碼本身內編譯,就像我這樣做:

BinaryExpression result = Expression.Add(arga, argb); 

這將彙編本:

.Lambda #Lambda1<System.Func`3[System.Int32,System.Int32,System.Int32]>(
    System.Int32 $var1, 
    System.Int32 $var2) { 
    $var1 + $var2 
} 

我還會注意到我不希望有工作過的對象實例,即使方法是非靜態的。我只想提取方法體,然後在我的代碼中內聯使用它。

我以某種方式認爲System.Reflection.Emit命名空間有我正在尋找的答案,但我不知道繼續哪個方向。

+0

我很確定使用'Expression','MethodBuilder','DynamicMethod'等生成的方法永遠不會被內聯。你能做的最好的事情實際上是在你的表達式中生成這個語句並編譯它,但我想這不會給你想要的。 – jamespconnor

+0

@jamespconnor正確,那是行不通的。這個想法是,如果我改變了具體方法中的代碼,生成的代碼將引入新的方法體。我不想在兩個地方重複邏輯,這樣我也必須修改表達式代碼以匹配具體方法正在做的事情。 – oscilatingcretin

+2

你可以創建'Expression'作爲'static',然後在兩個地方使用它?例如'private static readonly表達式> AddNumbers =(a,b)=> a + b;' – jamespconnor

回答

0

一個簡單的方法是以編程方式反彙編AddNumbers函數,然後使用該輸出來驅動Reflection.Emit以實時創建內聯代碼。

然而,你可能想要一些方法來在你的主(或任何將接收內聯代碼的方法)擁有佔位符來保存發出的代碼。換句話說,您可能不想排出Main的全部內容,您希望編譯器創建除AddNumbers的內聯代碼之外的所有內容。 (但是你可以在整個Main方法中應用相同的反編譯/重編譯方法)。

雖然這樣的問題需要預留多少空間?如果你內聯的代碼和AddNumbers一樣簡單,那就沒什麼問題了。您可以使用內聯代碼替換原始方法調用,因爲它會更短。但是,如果內聯代碼比AddNumbers要複雜得多,那麼您需要更多的空間用於內聯代碼,並且還可以讓編譯器留出足夠的空間。

但隨着內聯代碼變得越來越長,內聯節省的成本變得越來越小,所以也許你可能對代碼的大小有一個嚴格的限制 - 任何內聯的代碼都必須小於限制。

我想給一個更好的答案,你需要給有關問題的更多信息:

你希望編譯器生成的代碼和內嵌代碼在一個單一的方法進行混合?或者將一個單獨的,動態創建的方法工作?

如果是前者會有單個站點來保存內聯代碼?或者會有不止一個,而是固定的,少量的網站?

您預期內聯代碼的複雜程度(以IL指令的數量爲準)?

是動態生成和編譯C#代碼的一個選項?根據您對其他問題的回答,Reflection.Emit可能不是一個好選擇 - 相反,您可能需要編譯器的智能。

1

有沒有簡單的方法來做到這一點。

C#編譯器將您的方法編譯爲IL並將其轉換回表達式是可能的,但是很複雜,並且成功的級別可以變化。你可以使用現有反編譯器的代碼來幫助(例如ILSpy是開源的),甚至可能有一個庫(這是我發現的唯一的東西是this old library)。

但我的猜測是,所有這一切都不值得它只是爲了「減少煩躁,保持堆疊整齊」。

相關問題