2016-12-02 8 views
1

私は次の型階層を持っている:C#Create.Delegateは継承をサポートしていますか?

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

私は次のことを達成する必要があります。

  • (これはすでに私が入手Func<Parent>を呼び出すことによって解決され、実行時にParentの任意のインスタンスを作成します。
  • のすべてのメソッド(その場合は常にTaskを返し、stringを受け入れる)を呼び出して、を渡します。は定数ではありません。

は、上記のため、私は私がその後、キャッシュし、必要なときに使用しますデリゲートを作成することになりますので、起動時にCached Delegatesに頼らい性能を向上させるために、ホット・パスに存在します。 私が明示的に行った例ですが、デリゲートが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

!実際には、アセンブリを[ここ](http://stackoverflow.com/a/5160513/1226568)から取った '[assembly:SecurityTransparent]'としてマーキングすることで余分なオーバーヘッドを補うことができました。 CreateDelegate' version :-) – 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

これは興味深い答えですが、1つの問題は、特定のインスタンスで実行するためにはそれぞれの呼び出しを必要とすることです。ソリューションでは、1つのインスタンスを作成するたびに 'Delegate'を作成する必要があります。いいえ? – MaYaN

+0

はい、すべてのインスタンスに対してデリゲートを作成する必要があります。インスタンスを提供することにより、メソッドがインスタンスレベルで作成され、後で指定されたインスタンスで実行されるようになります。 [link](https://msdn.microsoft.com/en-us/library/74x8f551(v = vs.110).aspx)をご覧ください。 – Johnny

+0

明確化のおかげで、すべてのインスタンスに対してデリゲートを作成するパフォーマンスのために、このメソッドを使用することはできません。しかし、関係なくupvoted。 @パベルの答えはこれまでのところ私が探しているものです。 – MaYaN

関連する問題