2

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();
    }
}
Sinan AKYAZICI
  • 3,942
  • 5
  • 35
  • 60

2 Answers2

2

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.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
2

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);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thank you for your answer. But the method in the func must take a parameter from outside. I think @CodesInChaos is right. Probably, It is imposible. – Sinan AKYAZICI Oct 19 '12 at 12:40
  • @sinanakyazici indeed, I said "you cannot create that directly via `Delegate.CreateDelegate`". The rest was context on *why* you can't, and how you can work around it. If you **really care enough**, it could be done at runtime using `ILGenerator`, but it would be stupidly convoluted. – Marc Gravell Oct 19 '12 at 12:43
  • ILGenerator is very complex. I want to do in this way to I want its to be simple. So I can't use it. This concerns a part of my project. So I need to find a new thing for this part. – Sinan AKYAZICI Oct 19 '12 at 12:53
  • @sinanakyazici "I want its to be simple". What you ask **is not** simple; the compiler makes it *look* simple, but actually the capture-context code used in lambdas and anonymous-methods is some of the most complex code in the compiler. What you are trying to do has inherent complexity. – Marc Gravell Oct 19 '12 at 13:00