2016-12-02 60 views
1

我有以下類型層次:如何讓C#Create.Delegate支持繼承?

public abstract class Parent { } 
public class Child : Parent 
{ 
    public Task SayAsync(string arg) 
    { 
     Console.WriteLine(arg); 
     return Task.CompletedTask; 
    }  
} 

我需要實現以下目標:

  • 在運行時創建的Parent任何實例(這已經通過調用Func<Parent>我得到解決
  • 然後調用(在該實例中)所有的public方法(它總是返回Task並接受string作爲參數)傳入arg這不是一個常數。

在熱通道上面存在因此,爲了提高性能,我訴諸Cached Delegates所以在啓動我將創建代表我會再緩存並在需要時使用。

這裏是我已經明確地爲Child做了一個例子,但我不知道如何讓委託接受Parent(因爲我不知道編譯時的類型)。

// If I change Child to Parent, I get "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type" 
private delegate Task Invoker(Child instance, string arg); 

void Main() 
{ 
    var instance = new Child(); // This will be obtained by calling a Func<Parent> 
    var methodWithArg = instance.GetType().GetMethod("SayAsync"); 

    var func = GetDelegateWithArg(methodWithArg); 

    func(instance, "Foo"); 
} 

private static Invoker GetDelegateWithArg(MethodInfo method) 
{ 
    object pointer = null; 
    return (Invoker)Delegate.CreateDelegate(typeof(Invoker), pointer, method); 
} 

任何想法或替代方案,以幫助我實現目標,表示讚賞。

回答

2

您可以嘗試使用表達式樹,而不是產生代表:

public abstract class Parent { } 

public class Child : Parent 
{ 
    public Task SayAsync(string arg) 
    { 
     Console.WriteLine(arg); 
     return Task.CompletedTask; 
    } 
} 

public delegate Task Invoker(Parent instance, string arg); 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Parent instance = new Child(); // This will be obtained by calling a Func<Parent> 
     var methodWithArg = instance.GetType().GetMethod("SayAsync"); 

     var func = GetDelegateWithArg(methodWithArg); 

     func(instance, "Foo"); 
    } 

    private static Invoker GetDelegateWithArg(MethodInfo method) 
    { 
     var instanceParameter = Expression.Parameter(typeof(Parent)); 
     var argParameter = Expression.Parameter(typeof(string)); 
     var body = Expression.Call(
      Expression.Convert(instanceParameter, method.DeclaringType), 
      method, 
      argParameter); 
     var lambda = Expression.Lambda<Invoker>(body, instanceParameter, argParameter); 
     return lambda.Compile(); 
    } 
} 

產生的代表們還是非常快的,雖然可能比Delegate.CreateDelegate()方法創建慢一點點。

+0

這是真棒!事實上,我設法通過將程序集標記爲'[assembly:SecurityTransparent]'從[HERE](http://stackoverflow.com/a/5160513/1226568)中獲取補償額外開銷,它現在比'Delegate'更快。 CreateDelegate'版本:-) – MaYaN

1

也許嘗試將目標實例傳遞給GetDelegateWithArg方法,並更改Invoker的簽名以接收參數。事情是這樣的:

private delegate Task Invoker(string arg); 

private static Invoker GetDelegateWithArg(MethodInfo method, object target) 
{    
    return (Invoker)Delegate.CreateDelegate(typeof(Invoker), target, method); 
} 

建議的修改後,你可以通過實例來委派的創造者和incoke FUNC只是一個參數:

func("Foo"); 
+0

這是一個有趣的答案,但一個問題是,我需要每個調用來執行一個給定的實例,在你的解決方案中,我必須在每次創建一個實例時創建'Delegate'。沒有? – MaYaN

+0

是的,你必須爲每個實例創建委託。通過提供實例,您將確保該方法將在實例級別上創建,並稍後在給定實例上執行。你可以看看[link](https://msdn.microsoft.com/en-us/library/74x8f551(v = vs.110).aspx)。 – Johnny

+0

感謝您的澄清,由於創建每個實例的委託的性能常量,我將無法使用此方法。但不管怎樣,upvoted。 @帕維爾的答案到目前爲止我正在尋找。 – MaYaN