What is meant by "instance method closed over its first argument" in the C# Delegate class documentation

80 Views Asked by At

I'm currently working through the documentation of the Delegate class in C# and am running into some terminology that is tripping me up. I have a conceptual understanding of what a delegate is coming from C function pointers and C++ lambdas etc, its the fine details in C# I'm trying to pick up

The specific lines/terms from the documentation and the questions regarding them are listed below:

A delegate is an instance of a delegate type that has references to:

  • An instance method of a type and a target object assignable to that type. (emphasis mine)

I understand the instance method part. For example:

FooDelegateType foo = FooInstanceMethod;

What however is meant by "and a target object assignable to that type"? In the example above, does target object refer to a specific instance of the type containing the definition for FooInstanceMethod?

Next:

  • An instance method of a type, with the hidden this parameter exposed in the formal parameter list (emphasis mine). The delegate is said to be an open instance delegate.

I'm having a hard time visualizing what this means. Does it mean the instance method has an explicit "this" parameter, something such as the following:

public class Foo {
    public void FooMethod(Foo this) { // do thing }
}

I doubt the above sample is how you would formally expose the hidden "this" parameter. How is this properly done?

Third:

A static method.

All clear, no problems here.

Fourth:

A static method and a target object assignable to the first parameter of the method. (emphasis mine)

Similar to my first question, what is the target object assignable to the first parameter. Syntactically, what would that look like?

Lastly:

When a delegate represents an instance method closed over its first argument (the most common case), the delegate stores a reference to the method's entry point and a reference to an object, called the target, which is of a type assignable to the type that defined the method

What does it mean by "an instance method closed over its first argument".

Thank you!

2

There are 2 best solutions below

2
Guru Stron On

With the following Foo definition used:

class Foo
{
    public void Do() => Console.WriteLine("Test");

    public static void DoString(string s) => Console.WriteLine(s);
}

An instance method of a type and a target object assignable to that type. (emphasis mine)

This means that delegate represents invokable instance function with "captured" instance. For example:

Delegate myDel = new Foo().Do; // delegate stores the instance and the method 
myDel.DynamicInvoke(); // invoke with no parameters

Does it mean the instance method has an explicit "this" parameter, something such as the following:

No, it means that since basically instance methods have implicit this parameter (the "hidden" one) you can create an open instance delegate (representing function with first parameter of the instance type):

delegate void OpenInstDel(Foo p);

// use instance Foo.Do() func as func accepting instance of Foo
Delegate del = typeof(Foo).GetMethod(nameof(Foo.Do)).CreateDelegate(typeof(OpenInstDel));
// pass instance of Foo
del.DynamicInvoke(new Foo());

// or 

var del1 = (OpenInstDel)typeof(Foo).GetMethod(nameof(Foo.Do)).CreateDelegate(typeof(OpenInstDel));
// pass instance of Foo
del1(new Foo());

A static method and a target object assignable to the first parameter of the method. (emphasis mine)

This is similar to the previous one. You use a static function with a single parameter to create a delegate representing function with no parameters by closing it over some target:

var str = "qweeqwe";
// use DoString(string) with str as parameter
Delegate closedStatic = typeof(Foo).GetMethod(nameof(Foo.DoString), BindingFlags.Public | BindingFlags.Static)
    .CreateDelegate<Action>(str);

// no parameters
closedStatic.DynamicInvoke();

What does it mean by "an instance method closed over its first argument".

It means that the new Foo() in first example is captured by the delegate instance, hence if the instance goes out of the visibility scope but the delegate does not, the instance would not be garbage collected so the delegate can be invoked correctly. I.e. creating a so called closure.

1
Sweeper On

These are the four cases of the Method and Target properties. You can create all four "kinds" of delegates using the CreateDelegate(Type, Object, MethodInfo, Boolean) method.


An instance method of a type and a target object assignable to that type.

This is the case when Method is an instance method, and Target is not null.

Func<Type> x = "some string".GetType;

"some string" is the Target, and Method is the MethodInfo of GetType.


A static method

This is the case where Target is null, and Method is a static method. e.g.

Action x = Console.WriteLine;

A static method and a target object assignable to the first parameter of the method.

This is the case where Method is a static method, but Target is not null. This typically is the case when you write a method group of an extension method.

using System.Linq;
Func<char> x = "some string".First; // First is an extension method!

Method is First (a static method), and Target is "some string"


An instance method of a type, with the hidden this parameter exposed in the formal parameter list

This is the case where Method is an instance method, but Target is null. How can you call an instance method without a Target? Well, the delegate type has one extra parameter than the formal parameters of the instance method, and that parameter represents the instance on which you are calling the method, aka this.

Think about how an instance method is called:

someInstance.SomeMethod(x, y, z);

This case is basically treating someInstance as an extra "parameter", in addition to x, y, z.

I don't think there is a way to make such a delegate with method groups, but you can certainly do it with CreateDelegate.

var method = typeof(C).GetMethod("F");
var x = (Action<C>)Delegate.CreateDelegate(
    typeof(Action<C>), 
    null, // Target
    method, // Method
    true
);

x(new C()); // this is like calling new C().F()

public class C {
    private int x = 10;
    public void F() {
        Console.WriteLine(this.x);
    }
}

an instance method closed over its first argument

This is referring to the two cases where Target is not null.