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-- thePredicatemethod 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;