2010-12-01 35 views
3

我有一個我想調用的私有方法的結構。由於我打算在關鍵性能部分做到這一點,所以我想緩存一個委託來執行操作。問題是我似乎無法綁定到Delegate.CreateDelegate它的方法。有問題的結構不是我創建的,用於與第三方庫的交互。 有問題的結構看起來像這樣::如何從結構的實例方法創建一個開放的委託?

public struct A 
{ 
    private int SomeMethod() 
    { 
     //body go here 
    } 
} 

而下面的代碼將失敗,一個「錯誤綁定到目標方法」。

Delegate.CreateDelegate(typeof(Func<A,int>),typeof(A).GetMethod("SomeMethod",BindingFlags.Instance | BindingFlags.NonPublic)); 

我知道我可以寫一個表達式樹要執行的操作,但它似乎很奇怪,我不能用我的正常轉到這些東西Delegate.CreateDelegate方法。

如果A是一個類,上面的代碼工作得很好。這個問題只是因爲A是一個結構。 對於CreateDelegate的重載,MSDN文檔不正確,因爲它在非靜態方法上工作。

+0

出於好奇,爲什麼要使用結構實例方法?使用結構實例方法創建委託將有效地需要裝箱結構以便使用它,於是盒裝結構實例將有效地表現爲類類型。爲什麼不直接使用類(即使類只包含一個結構類型的字段)? – supercat 2013-02-01 23:47:42

+0

這是一個老問題,但結構來自第三方庫。但是,使用委託不會複製它,因爲結構實例方法是「by-ref」。我需要在他們給我的句柄上調用一個方法。因爲他們只是在最後叫它,但我想早些時候做(我不記得)。 – 2013-02-05 15:58:46

+0

我想如果你打算將結構方法視爲一個靜態方法,它採用`ref`參數(而不是從未綁定委託創建綁定委託),你可以避免裝箱,儘管在這種情況下我認爲使用靜態結構方法會更清晰。 – supercat 2013-02-05 16:15:08

回答

7

有趣的問題。從這個錯誤報告,它看起來像這可能是將被固定在.NET的未來版本中的錯誤: http://connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance-delegate-for-value-types-methods-which-implement-an-interface#details

編輯:其實,我覺得這個bug報告是關於一個不同的問題,所以你的行爲看到的可能並不是一個錯誤。

從該錯誤報告中,我收集到,如果您指定委託的第一個參數爲引用傳遞,則會有解決方法。下面是一個完整的工作示例:

public struct A 
{ 
    private int _Value; 

    public int Value 
    { 
     get { return _Value; } 
     set { _Value = value; } 
    } 

    private int SomeMethod() 
    { 
     return _Value; 
    } 
} 

delegate int SomeMethodHandler(ref A instance); 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var method = typeof(A).GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic); 

     SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method); 

     A instance = new A(); 

     instance.Value = 5; 

     Console.WriteLine(d(ref instance)); 
    } 
} 

編輯:Jon Skeet's answer here也討論了這個問題。

0

您使用this overload of CreateDelegate

創建指定類型代表指定靜態方法的委託。

SomeMethod不是一個靜態方法。

使用overload that allows to specify a target object

A target = new A(); 

Func<int> f = (Func<int>)Delegate.CreateDelegate(
    typeof(Func<int>), 
    target, 
    typeof(A).GetMethod(
     "SomeMethod", 
     BindingFlags.Instance | BindingFlags.NonPublic)); 

這意味着你需要,雖然創造的A每個實例的委託。你不能在不同的實例中重複使用同一個委託。

最好的解決辦法可能是使用LINQ表達式樹來構建一個Lambda表達式:

var p = Expression.Parameter(typeof(A), "arg"); 
var lambda = Expression.Lambda<Func<A, int>>(
    Expression.Call(
     p, 
     typeof(A).GetMethod(
      "SomeMethod", 
      BindingFlags.Instance | BindingFlags.NonPublic)), 
    p); 

Func<A, int> f = lambda.Compile(); 
1

未綁定實例方法委託的第一個參數不能是值類型。這是因爲當用作'this'參數時必須處理值類型。您不能簡單地按值傳遞它們(如果您將值類型作爲靜態方法的第一個參數傳遞,那麼會發生這種情況),因爲方法在副本上進行操作,並且副本的任何突變都不會影響原始。


UPDATE:如在另一個答案所指出的,「本次」參數被有效地通過引用傳遞使用時值類型。

相關問題