I see two areas of different behavior that arise from the two mechanisms:
- The pointer object is a separate object from the referenced function, and will have a separate identity from the function, and from other pointers to the function
- The pointer object can have behaviors beyond that of the original function. For example, in the CLI:
- Delegate instances know about the relevant
this
(and can bind it to the method)
- Delegate instances can contain pointers to multiple functions
(Note: This is from my experience in C# / VB.NET and Javascript. Other languages may have different variants of either mechanism.)
Object reference equality
If a function is a "real object" then any variables pointing to the function are actually pointing to the same object:
//Javascript
var fn = console.log;
var fn1 = console.log;
console.log(fn === fn1); //prints true
Pointer objects have their own identity, even when both point to the same function:
//C#
Action action = Console.WriteLine;
Action action2 = Console.WriteLine;
Console.WriteLine(Object.ReferenceEquals(action, action2)); //prints False
Additional behaviors of the pointer object
Target binding
Function objects have no knowledge of the class to which they are attached:
//Javascript
var a1 = {
data: 5,
writeData: function() {
'use strict'; //otherwise `this` would be the global object; `this.data` would probably return `undefined`
console.log(this.data);
}
};
var action = a1.writeData;
action(); //Uncaught TypeError: Cannot read property 'data' of undefined
Therefore, part of calling the function as an instance method, is the implict binding of this
within the function to the object:
a1.writeData(); //prints 5
We can also explicitly bind this
with bind
, apply
, or call
:
action = a1.writeData.bind(a1);
action(); //prints 5
However (as @amon pointed out in this answer), the delegate instance retains that information:
//C#
public class A {
public int Data;
public void WriteData() {
Console.WriteLine(this.Data);
}
}
var a1 = new A() { Data=4 };
Action action = a1.WriteData;
because action
contains knowledge of the target of methods:
Console.WriteLine(action.Target == a1); //prints True
Multicast delegate
Javascript variables / properties that refer to a function object, work just like references to any other object, and therefore cannot refer to multiple function objects simultaneously.
On the other hand, a delegate instance in .NET can point to multiple functions:
//C#
public static class Writers {
public static void WriteOne() {
Console.WriteLine(1);
}
public static void WriteTwo() {
Console.WriteLine(2);
}
}
action = Writers.WriteOne;
action += Writers.WriteTwo;
action(); //prints 1, and then prints 2
var f = Console.WriteLine
(note the dropped parenthesis), although I'm not sure type inference works here, you might have to specify the type asAction<string>
, or similar depending on the overload you want.Action<string> f = Console.WriteLine;
works just fine. you can also send methods as parameters to other methods like this (a central piece in LINQ). e.g.bool Predicate(A a) { return true; }
new A[] {}.Where(Predicate);
Console.WriteLine
(to either to a variable or a parameter) creates a delegate instance which internally points toConsole.WriteLine
. The method itself is not being passed. Otherwise, ifAction<string> f2 = Console.WriteLine;
, why doesObject.ReferenceEquals(f,f2);
return false?.Where
-- thePredicate
method is being wrapped in a delegate instance which has its own properties and methods. You cannot writevar a = Predicate.Target;
but you can writeFunc<A,bool> predicateDelegate = Predicate; var a = predicateDelegate.Target;