I tried disassembling a C# created executable, but I couldn't come to a conclusion. What I'd like to know is that if for the CLR c#'s delegates are really special entities or just a compiler sugar?
I ask this because I'm implementing a language that compiles to C#, and it would be much more interesting for me to compile anonymous functions as classes than as delegates. But I don't want to use a design that later I will regret, as they might be heavier on memory (I think of Java's PermGen to base my questioning on. Even though I know there is no such thing for the CLR).
Thank you!
--edit
to be a little more clear, I'd like to know if there is (and what are) the differences between:
void Main()
{
Func<int, int, int> add = delegate(int a, int b) {return a + b;};
}
and, for example
class AnonFuncion__1219023 : Fun3
{
public override int invoke(int a, int b)
{
return a + b;
}
}
-- edit
I think that there might be a great difference between:
class Program
{
static int add(int a, int b)
{
return a + b;
}
static void Main()
{
Func<int, int, int> add = Program.add;
}
}
and
class Function__432892 : Fun3
{
public override int invoke(int a, int b)
{
return Program.add(a, b);
}
}
I read somewhere, though, that the syntax Func<int, int, int> add = Program.add;
is only a sugar for Func<int, int, int> add = delegate(int a, int b) { return Program.add; };
. But I really don't know if that's really true.
I could also see that the C# compiler already caches all those instances so they are constructed only once. I could do the same with my compiler, though.
Which of the following statements are correct about delegates? Delegates cannot be used to call a static method of a class. Delegates cannot be used to call procedures that receive variable number of arguments. If signatures of two methods are same they can be called through the same delegate object.
A delegate is a reference type variable that holds the reference to a method. An event is a delegate type class member used by the object or class to provide notification to other objects that an event has occurred.
Delegates are the library class in System namespace. These are the type-safe pointer of any method. Delegates are mainly used in implementing the call-back methods and events. Delegates can be chained together as two or more methods can be called on a single event.
Delegates allow methods to be passed as parameters. Delegates can be used to define callback methods. Delegates can be chained together; for example, multiple methods can be called on a single event. Methods don't have to match the delegate type exactly.
I'm surprised you couldn't come to a conclusion by disassembling an executable. Let's take a look at something really simple:
using System;
class A
{
int _x;
public A(int x)
{
_x = x;
}
public void Print(int y)
{
Console.WriteLine(_x + y);
}
}
interface IPseudoDelegateVoidInt
{
void Call(int y);
}
class PseudoDelegateAPrint : IPseudoDelegateVoidInt
{
A _target;
public PseudoDelegateAPrint(A target)
{
_target = target;
}
public void Call(int y)
{
_target.Print(y);
}
}
class Program
{
delegate void RealVoidIntDelegate(int x);
static void Main()
{
A a = new A(5);
IPseudoDelegateVoidInt pdelegate = new PseudoDelegateAPrint(a);
RealVoidIntDelegate rdelegate = new RealVoidIntDelegate(a.Print);
pdelegate.Call(2);
rdelegate(2);
}
}
If we disassemble this we get
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly del
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module del.exe
// MVID: {87A2A843-A5F2-4D40-A96D-9940579DE26E}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x0000000000B60000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit A
extends [mscorlib]System.Object
{
.field private int32 _x
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 x) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld int32 A::_x
IL_000f: nop
IL_0010: ret
} // end of method A::.ctor
.method public hidebysig instance void
Print(int32 y) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 A::_x
IL_0007: ldarg.1
IL_0008: add
IL_0009: call void [mscorlib]System.Console::WriteLine(int32)
IL_000e: nop
IL_000f: ret
} // end of method A::Print
} // end of class A
.class interface private abstract auto ansi IPseudoDelegateVoidInt
{
.method public hidebysig newslot abstract virtual
instance void Call(int32 y) cil managed
{
} // end of method IPseudoDelegateVoidInt::Call
} // end of class IPseudoDelegateVoidInt
.class private auto ansi beforefieldinit PseudoDelegateAPrint
extends [mscorlib]System.Object
implements IPseudoDelegateVoidInt
{
.field private class A _target
.method public hidebysig specialname rtspecialname
instance void .ctor(class A target) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld class A PseudoDelegateAPrint::_target
IL_000f: nop
IL_0010: ret
} // end of method PseudoDelegateAPrint::.ctor
.method public hidebysig newslot virtual final
instance void Call(int32 y) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class A PseudoDelegateAPrint::_target
IL_0007: ldarg.1
IL_0008: callvirt instance void A::Print(int32)
IL_000d: nop
IL_000e: ret
} // end of method PseudoDelegateAPrint::Call
} // end of class PseudoDelegateAPrint
.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
.class auto ansi sealed nested private RealVoidIntDelegate
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
} // end of method RealVoidIntDelegate::.ctor
.method public hidebysig newslot virtual
instance void Invoke(int32 x) runtime managed
{
} // end of method RealVoidIntDelegate::Invoke
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult
BeginInvoke(int32 x,
class [mscorlib]System.AsyncCallback callback,
object 'object') runtime managed
{
} // end of method RealVoidIntDelegate::BeginInvoke
.method public hidebysig newslot virtual
instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
} // end of method RealVoidIntDelegate::EndInvoke
} // end of class RealVoidIntDelegate
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 45 (0x2d)
.maxstack 3
.locals init (class A V_0,
class IPseudoDelegateVoidInt V_1,
class Program/RealVoidIntDelegate V_2)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newobj instance void A::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: newobj instance void PseudoDelegateAPrint::.ctor(class A)
IL_000e: stloc.1
IL_000f: ldloc.0
IL_0010: ldftn instance void A::Print(int32)
IL_0016: newobj instance void Program/RealVoidIntDelegate::.ctor(object,
native int)
IL_001b: stloc.2
IL_001c: ldloc.1
IL_001d: ldc.i4.2
IL_001e: callvirt instance void IPseudoDelegateVoidInt::Call(int32)
IL_0023: nop
IL_0024: ldloc.2
IL_0025: ldc.i4.2
IL_0026: callvirt instance void Program/RealVoidIntDelegate::Invoke(int32)
IL_002b: nop
IL_002c: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file C:\Users\logan\del.res
As you can see the RealVoidIntDelegate becomes "just" another class. They called it Invoke
instead of Call
, and they didn't use an interface but there are no special instructions involved, it is the same basic idea.
To elaborate a little bit on the idea of "lightweightness", delegates are not lighter weight than classes because a given delegate is a class. Especially in the case of function literal syntax, they really can't be much lighter weight. However for the "call this method on this object with these args" case invoking and creating a given delegate will probably be lighter weight then a home grown delegate when compiling down to C#.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With