How to define a delegate using Delegate.CreateDele

2019-07-21 20:23发布

I have a method and two delegate like below. It is running in this way. But I want to use Delegate.CreateInstance. The types of the dx and the dy must be Func<IEnumerable<Foo>>. Like below the fx and fy. They must not be Func<int, IEnumerable<Foo>>.

public class Test {
    private IEnumerable<T> CreateItems<T>(int count) where T : class
    {
        for (int i = 0; i < count; i++)
        {
            yield return (T)Activator.CreateInstance(typeof(T), i.ToString());
        }
    }

    public List<T> TestMethod<T>(int i = 1) where T : class
    {
        return CreateItems<T>(i).ToList();
    }

    public void TestRun()
    {
        const int Count = 5;
        Func<IEnumerable<Foo>> fx = () => this.TestMethod<Foo>(Count);
        Func<IEnumerable<Foo>> fy = () => this.TestMethod<Foo>();
        var lfx = fx.Invoke();
        var lfy = fy.Invoke();
        var dx = Delegate.CreateDelegate( ?? );
        var dy = Delegate.CreateDelegate( ?? );
        var ldx = dx.DynamicInvoke();
        var ldy = dy.DynamicInvoke();
    }
}

2条回答
贼婆χ
2楼-- · 2019-07-21 21:02

If you want the type to be Func<IEnumerable<Foo>>, then you cannot create that directly via Delegate.CreateDelegate since they require two parameters: the instance (aka this), and the integer i. Even the form shown in fx has an i - it just happens to be supplied by the compiler. If TestMethod didn't take parameters, it could be done via:

var dy = (Func<IEnumerable<Foo>>) Delegate.CreateDelegate(
    typeof(Func<IEnumerable<Foo>>),
    this,
    GetType().GetMethod("TestMethod").MakeGenericMethod(typeof(Foo))
);

To do this (partial application) dynamically, you would need to create a type that has the instance (this), the value to inject (the i), and a method that calls TestMethod<Foo> with those values. Which is exactly what the compiler does for you here:

Func<IEnumerable<Foo>> fx = () => this.TestMethod<Foo>(Count);

That basically creates:

internal class <>_squiggle {
    public Test @this;
    public IEnumerable<Foo> Method() {
        return @this.TestMethod<Foo>(5);
    }
}

and:

var capture = new <>_squiggle { @this = this };
var fx = new Func<IEnumerable<Foo>>(capture.Method);
查看更多
甜甜的少女心
3楼-- · 2019-07-21 21:13

That's impossible. There is no way you can fit an instance method with signature A F(X x) into a Func<A> directly.

It's possible to bind the first parameter of a method into the delegate directly, but no additional parameters. In your case the instance this is that first parameter, and you can't bind a value for i.

I guess your misunderstanding is how parameters with default values work. They're still parameters that need to be filled in by the caller. It's just that the C# compiler does that for you.

You will need a wrapper of some kind with the correct signature. That can be a lambda, or some other helper method. In your case I'd overload the method TestMethod instead of using a default parameter.

查看更多
登录 后发表回答