Chapter 5
Delegates and Events
A delegate is extremely important
for C# as it is one of the four entities that can be placed in a namespace.
This makes it shareable among classes. Delegates are fully object oriented as
they entirely enclose or encapsulate an object instance and a method. A
delegate defines a class and extends System.Delegate. It can call any function
as long as the methods signature matches the delegates. This makes delegates
ideal for anonymous invocation. The methods signature only includes the return
type and the parameter list. If two delegates have the same parameter and
return type, that is, they share the same signature, we consider them as
different delegates. This chapter will feature these issues using simple
programs.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate void pqr();
void pqr1 ()
{
System.Console.WriteLine("pqr1");
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
}
}
Output
pqr1
a is an object that looks like
aa. a.abc will call the abc function from this class. Earlier on, we specified
that new must be followed by the class. We stand corrected as usual here.
Likewise, we can also use a delegate name after new. A delegate is like a
property or an indexer. It is a first class member of a class. It looks like a
function, but has the keyword delegate in front of it along with the return
type of void.
When we use new to instantiate a
delegate, we pass it one parameter. This happens to be the name of a function,
namely, pqr1. This function is created in the same class, that is, aa, it takes
no parameters and returns a void. The keyword new obviously creates an object
that looks like a delegate as it was given a delegate as a datatype. d is now
an object that looks like pqr and stores the delegate object of pqr1. Then we
use the function invocation syntax and write d(). To our utter surprise, we
have actually executed the function pqr1 without writing pqr1().
The reasoning here is that d is a
delegate object for function pqr1 as we have passed it as a parameter at the
time of creation. Calling d calls function pqr1 instead. This is one level of
abstraction thus helping us write complicated code.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate void pqr();
void pqr1 ()
{
System.Console.WriteLine("pqr");
}
public void abc()
{
pqr d = new pqr(pqr1);
d(100);
}
}
Compiler Error
a.cs(19,1): error CS1593: Delegate 'pqr' does not take '1'
arguments
C# does not let you do whatever
your heart desires. The delegate object d that looks like a pqr is now passed
one parameter, that is, 100. As d stands for the function pqr1 and pqr1 does
not accept an int as a parameter, an error is shown. This proves that C# does
strict type checking on the code.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate void pqr();
void pqr1 (int d)
{
System.Console.WriteLine("pqr");
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
}
}
Compiler Error
a.cs(18,18): error CS0123: Method 'aa.pqr1(int)' does not
match delegate 'void aa.pqr()'
In our infinite wisdom, we
thought that the above error will vanish if we added an int as a parameter to
our function pqr1. C# however tends to disagree and now gives us different
error message.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate int pqr(int ddd);
int pqr1 (int dd)
{
return dd * 2;
}
int pqr2 (int dd)
{
return dd * 10;
}
pqr d;
public void abc()
{
d = new pqr(pqr1);
int d1 = d(42);
System.Console.WriteLine(d1);
d = new pqr(pqr2);
int d2 = d(42);
System.Console.WriteLine(d2);
}
}
Output
84
420
To eliminate the error message,
we added an int parameter to the delegate pqr. This informs the C# compiler
that whoever calls the delegate pqr, will hand it one parameter. The delegate
now returns an int unlike the earlier code where we returned void. Then C#
checks whether the function name given at the time of creation of the delegate
accepts one parameter and returns an int. Finally, when we run the function
through the delegate, a last check is made to see that the right parameters are
given.
We have also added a tweeze of
lime to our program. We have created one more delegate type but now it is with
another function name as its parameter. The code remains the same. We keep
executing the delegate as d(42), but the function to be called changes each
time. This offers us greater flexibility in writing dynamic code.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate int pqr();
void pqr1 ()
{
System.Console.WriteLine("pqr");
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
}
}
Compiler Error
a.cs(18,18): error CS0123: Method 'aa.pqr1()' does not match
delegate 'int aa.pqr()'
C# is very fussy about a
delegate. The return value of the delegate in the definition says that it will
return an int but the function pqr1 returns a void instead.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate int pqr(int ddd);
int pqr1 (int dd)
{
return dd * 2;
}
int pqr2 (int dd)
{
return dd * 10;
}
public void xyz( pqr a)
{
int f = a(10);
System.Console.WriteLine(f);
}
public void abc()
{
pqr d = new pqr(pqr1);
xyz(d);
d = new pqr(pqr2);
xyz(d);
}
}
Output
20
100
This function demonstrates a
simple fact that a delegate is like a class. We can give objects that look like
classes as parameters to functions. Int, long etc are classes in C#. A delegate
being a data type can be passed as a parameter to a function.
In the first and second
invocation of function xyz we are passing the same delegate type of d. The
first time it stands for the function pqr and the second time function pqr2.
Using the object a, we are executing a different function each time. The code
of the function xyz remains the same. We are indirectly giving it a different
function each time. The code of function xyz does not know nor care about the
function it is calling.
Writing generic code like the
above, helps isolate the code implementation from its execution. More on these
abstract issues in a short while.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public class aa
{
public delegate int pqr(int ddd);
int pqr1 (int dd)
{
return dd * 2;
}
int pqr2 (int dd)
{
return dd * 10;
}
public void xyz( pqr a)
{
for ( int i = 1; i<=3 ; i++)
System.Console.Write(a(i) + " ");
}
public void abc()
{
pqr d = new pqr(pqr1);
xyz(d);
d = new pqr(pqr2);
xyz(d);
}
}
Output
2 4 6 10 20 30
The above program takes the
abstract behavior of a delegate a step further. Nothing on earth stops you from
executing a delegate through a loop construct. What we are repeating over and
over again is that a delegate passed as a parameter to a function, does not
know the name of the function it is going to execute at the time of
compilation. It will only receive this information at the time of execution
i.e. at run time.
a.cs
public class zzz {
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate void pqr();
public class aa
{
void pqr1 ()
{
System.Console.WriteLine("pqr1");
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
}
}
Output
pqr1
pqr is a delegate as it defines a
class that extends System.Delegate.
a.cs
public delegate void pqr();
public class aa : pqr
{
}
Compilation Error
a.cs(2,14): error CS0509: 'aa' : cannot inherit from sealed
class 'pqr'
A delegate internally is
represented by a class with the same name. In the above case, the class pqr is
implicitly sealed. A sealed class cannot be used in a derivation. A long time
ago we had explained to you that there were four classes you could not derive
from. If memory serves you well, one of them was System.Delegate and it is an
abstract class. All delegates derive from it automatically. We can access all
the members of System.Delegate in the normal way.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate void pqr();
public class aa
{
void pqr1 ()
{
System.Console.WriteLine("pqr1");
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
System.Console.WriteLine(d.ToString());
}
}
Output
pqr1
pqr
System.Delegate is also derived
from Object and hence using the syntax learnt earlier of member access, we can
call the ToString function of d.
a.cs
public delegate void pqr();
public class aa
{
void abc()
{
System.Console.WriteLine(pqr.ToString());
}
}
Compiler Error
a.cs(6,26): error CS0120: An object reference is required for
the nonstatic field, method, or property 'object.ToString()'
We cannot use the name of the
delegate pqr with function ToString as it is not static. d being an instance of
the class can be used and will give no errors.
Delegate types come in two sizes,
combinable and non-combinable.
a.cs
using System;
delegate void ddd(string s);
class zzz
{
public static void a1(string s)
{
Console.WriteLine(s + " a1");
}
public static void a2(string s)
{
Console.WriteLine(s + " a2");
}
public static void Main()
{
ddd a, b, c, d;
a = new ddd(a1);
b = new ddd(a2);
c = a + b;
d = a - b;
a("A");
b("B");
c("C");
d("D");
d = c - a;
d("E");
d = c - b;
d("F");
}
}
Output
A a1
B a2
C a1
C a2
D a1
E a2
F a1
This example proves the
importance of delegates in C# and they can be made extremely complex if you so
choose to. Objects a, b, c and d are delegate type objects. Object a and b
represent functions a1 and a2 respectively. Thus a("A") and
b("B") call functions a1 and a2.
This is shown above.
Object c represents the summation
of objects a and b. Obviously we cannot add two delegate types as they are not
numbers. The only rational explanation possible is that we want both the delegates
to be executed. First, function a1 gets called and then a2. The object d is
initialized to a - b. This will eliminate functions contained in a2 from a1. As
we have no common function in a2, it makes no difference and function a1 is
called.
The next series of statements
explains the operations better. The object d is made equal to c-a. This will
subtract all the functions that delegate a represents from the object c. The
object c stands for functions a1 and a2, and as a represents function a1, function
a1 gets removed from d. Thus a2 is called. In the last case of c - b, had we
executed the object c, both functions a1 and a2 would get called. But as we are
subtracting b i.e. function a2, only a1 gets called.
You can call as many functions
you want through delegates.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate int pqr();
public class aa
{
int pqr1 ()
{
System.Console.WriteLine("pqr1");
return 0;
}
public void abc()
{
pqr d = new pqr(pqr1);
pqr e = new pqr(pqr1);
pqr f = d + e;
}
}
Two delegates can be combined
provided the return type of each is not void. As our newly defined delegate,
pqr, returns an int, we get no errors.
a.cs
public class zzz {
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate int pqr(out int i);
public class aa
{
int pqr1 (out int i)
{
System.Console.WriteLine("pqr1");
i = 10;
return 0;
}
public void abc()
{
pqr d = new pqr(pqr1);
pqr e = new pqr(pqr1);
pqr f = d + e;
int k;
f(out k);
}
}
Output
pqr1
pqr1
Aha! A delegate function can
accept output parameters. Hence we see the output displayed as pqr1.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate int pqr(out int i);
public class aa
{
int pqr1 (out int i)
{
i = 10;
System.Console.WriteLine("pqr1 " + i);
return 0;
}
public void abc()
{
pqr d = new pqr(pqr1);
pqr e = null;
pqr f = d + e;
int k;
f(out k);
}
}
Output
pqr1 10
The same program now works. The
only change made is we have initialized one of the delegates to null. Now the
rules of simple delegates come into play. The rules of multi-cast delegates are
more restrictive. This also proves the hypothesis that multi-cast delegates are
not part of the programming language as the compiler did not catch the error.
As the compiler ignored it, it means that it is not part and parcel of the
language definition.
System.Delegate introduces a lot
of extra code in the delegate. It is this code that actually performs error
checks and if we break any rules it throws an error at run time. The next
version of System.Delegate and not C# may handle these errors gracefully.
Two different delegates can refer
to the same method and target object. The plus operator thus lets you combine
delegates and the subtraction operator lets you remove one delegate from
another. We do have to stop at two delegates only. When we execute a multi-cast
delegate, the effect is similar when invoking an ordered list of non-multicast
delegates.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
try
{
a.abc();
}
catch ( System.Exception e)
{
System.Console.WriteLine("in main");
}
}
}
public delegate void pqr();
public class aa
{
void pqr1()
{
throw new System.Exception();
}
public void abc()
{
pqr d = new pqr(pqr1);
d();
}
}
Output
in main
In the above program, d() calls
function pqr1. This function throws an exception. We could have caught the
exception using try catch in function abc itself but we chose not to do so.
Instead, we catch it in main where abc is called. The exception thrown in the
function called by the delegate moves up the ladder until it finally is caught.
If the catch clause is ignored, it generates a run time error as below.
Ouput
Exception occurred: System.Exception: An exception of type
System.Exception was thrown.
at aa.pqr1()
at zzz.Main()
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate void pqr(ref int i);
public class aa
{
void pqr1 (ref int i)
{
i = i + 2;
System.Console.WriteLine("pqr1 " + i);
}
public void abc()
{
pqr d = new pqr(pqr1);
pqr e = new pqr(pqr1);
pqr f = d + e;
int k = 3;
f(ref k);
}
}
Output
pqr1 5
pqr1 7
Earlier, we discussed about the
out parameters and concluded that they are not allowed in a multicast delegate.
Conversely, passing by ref is permissible in multi-cast delegate invocations.
If the called function changes k, the change is transmitted straight through.
Thus every invocation of pqr1 refers to the same variable k each time.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate void pqr();
public class aa
{
public void pqr1()
{
System.Console.WriteLine("pqr1");
throw new System.Exception();
}
public void pqr2()
{
System.Console.WriteLine("pqr2");
}
public void abc()
{
pqr d = new pqr(pqr1);
pqr e = new pqr(pqr2);
pqr f = d+e;
f();
}
}
Output
pqr1
Exception occurred: System.Exception: An exception of type
System.Exception was thrown.
at aa.pqr1()
at pqr.Invoke()
at aa.abc()
at zzz.Main()
If an exception is thrown during
a multi-delegate invocation, no more code gets called, processing simply halts.
Everything comes to a standstill. The function pqr1 gets called first. As it
throws an exception, C# does not call function pqr2, that it was supposed to
after finishing with function pqr1.
a.cs
public class zzz
{
public static void Main()
{
aa a = new aa();
a.abc();
}
}
public delegate void pqr();
public class aa
{
void pqr1 ()
{
System.Console.WriteLine("pqr1");
}
public void abc()
{
pqr d = new pqr(pqr1);
bb b = new bb();
pqr e = new pqr(b.xyz);
pqr f = d + e;
f();
}
}
public class bb
{
public void xyz()
{
System.Console.WriteLine("xyz");
}
}
Output
pqr1
xyz
A delegate is not confined to
executing code in its own class as its definition can be placed outside a
class. Here the delegate invocation list invokes two functions, one from the
same class pqr1 and the other function xyz from the class bb. Thus, the
delegate not only ignores the function it is calling but also the class from
which the function is called. It believes in equality thereby does not
differentiate code from one class to another.
Events
a.cs
class zzz {
public static void Main() {
yyy l = new yyy();
l.abc();
}
}
public class yyy
{
public void abc()
{
System.Console.WriteLine("hi");
}
}
Output
hi
We start with a very simple
program. In function Main we create an object l that
looks like class yyy. Then we simply call a function abc from it. In this case,
we are calling the function abc directly. There is no entity in between. Lets
see how we can call this function indirectly.
a.cs
public delegate void ddd();
class zzz {
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
l.a1();
}
}
public class yyy
{
public event ddd c;
public void a1()
{
if (c != null)
abc();
}
public void abc()
{
System.Console.WriteLine("hi");
}
}
Output
hi
We are doing exactly the same
thing as we did earlier but now its with a lot more fanfare. We started out by
creating a delegate ddd. We then instantiated an object that looks like the delegate
ddd and passed it one parameter, the name of the function. In our specific case
it is abc. As abc resides in a class yyy and we are operating in function Main
within class zzz, we have no choice but to give the full name, l.abc. The object c on the left hand side is not a
delegate but an event. Also the syntax is += and not = as it would give us the
following error.
Compiler Error
a.cs(5,3): error CS0070: The event 'yyy.c' can only appear on
the left hand side of += or -= (except when used from within the type 'yyy')
The object c has an odd-looking
definition. Earlier we stored the return value of the instantiation of a
delegate in a delegate object, here we have added the keyword event to the
object. Now we call function a1 from Main. In a1, we are checking the value of
the object c before calling abc. If it is not null, then we call the function
abc, otherwise we don't.
If we comment out the line new
ddd, then the object c does not get initialized. It will retain its value of
null and the function abc will not be executed.
a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
l.a1();
l.c -= new ddd(l.abc);
l.a1();
}
}
public class yyy
{
public event ddd c;
public void a1()
{
if (c != null)
abc();
}
public void abc()
{
System.Console.WriteLine("hi");
}
}
Output
hi
The first time we execute l.a1, the object c has a value, it is not null. On the
next line we have -= where we are subtracting the delegate ddd from the event
c. The delegate stands for the function abc in each case. On subtraction, this
common function is eliminated and the event object c now has a value null. Thus
we added the function abc in the first round and removed it in the second
round.
a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
l.c();
}
}
public class yyy
{
public event ddd c;
public void abc()
{
System.Console.WriteLine("hi");
}
}
Compiler Error
a.cs(8,3): error CS0070: The event 'yyy.c' can only appear on
the left hand side of += or -= (except when used from within the type 'yyy')
Unlike a delegate, an event
object cannot be used directly. Earlier, we used to execute a function through
the delegate object. That is not permitted with events. Events are employed for
notification purposes only.
a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
l.pqr();
}
}
public class yyy
{
public event ddd c;
public void pqr()
{
c();
}
public void abc()
{
System.Console.WriteLine("abc");
}
}
Output
abc
If you are still ignorant about
the use of an event, you are totally blameless. Whatever we have done so far
could have easily been done without events. In the above case, we have
associated an event c with a function abc in class yyy. We then execute
function pqr by giving c(). This executes the event object c. Earlier, a
similar thing was not possible as we were not in the same class. Now as the
event object c stands for a function abc, it calls that function.
The next program demonstrates
that we can call as many functions we want.
a.cs
public delegate void ddd();
class zzz {
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
l.c += new ddd(l.xyz);
l.pqr();
l.c -= new ddd(l.xyz);
l.pqr();
l.c -= new ddd(l.abc);
l.pqr();
}
}
public class yyy
{
public event ddd c;
public void pqr()
{
c();
}
public void abc()
{
System.Console.WriteLine("abc");
}
public void xyz()
{
System.Console.WriteLine("xyz");
}
}
Output
abc
xyz
abc
Unhandled Exception: System.NullReferenceException: Value null
was found where an instance of an object was required.
at zzz.Main()
This example reminds us of
delegates. We are adding two functions to the event c. Thus the first time we
call pqr, we are only executing the event c. The next time we will call the abc
and pqr functions as they are now bound to the event. A += adds a function and
a -= subtracts/removes a function from the event list. Now when we call the
event c, only function abc will be called as xyz has been removed from the
list. Finally we are also removing abc from the list and the event will have a
value null.
Whenever we execute an event that
has no functions to notify, a run time exception is generated. Thus always
check if the event has some value and is not null before using it in your code.
a.cs
public delegate void ddd();
class zzz {
public static void Main()
{
yyy l = new xxx();
}
}
public class yyy
{
public event ddd c;
public void abc()
{
System.Console.WriteLine("abc");
}
}
public class xxx : yyy
{
public void pqr()
{
c();
}
}
Compiler Error
a.cs(20,1): error CS0070: The event 'yyy.c' can only appear on
the left hand side of += or -= (except when used from within the type 'yyy')
An event is used to call a
function in the same class. Here even though xxx is derived from yyy, they do
not belong to the same class. Hence an event created in class yyy cannot be
used in any class other than yyy, even derived classes are exempted. Normally
C# allows derived classes to inherit the workings of the base class. Events are
one of the many exceptions and hence the error is generated at compile time,
not at run time.
a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
yyy l = new yyy();
l.c += new ddd(l.abc);
xxx x = new xxx();
l.c += new ddd(x.xyz);
l.pqr();
}
}
public class yyy
{
public event ddd c;
public void pqr()
{
c();
}
public void abc()
{
System.Console.WriteLine("abc");
}
}
public class xxx
{
public void xyz()
{
System.Console.WriteLine("xyz");
}
}
Output
abc
xyz
The power of events spreads on
code across classes. We've used the same code as in the earlier porgram and
given the name of a function, xyz. This function does not belong to yyy but is
taken from xxx. The event call does not seem to bother and acts accordingly.
A similar example was shown with
delegates. The same holds true for events too.
a.cs
public delegate void ddd();
public class zzz
{
public event ddd c;
//public ddd c;
public void add_c( ddd a)
{
c += a;
}
public void remove_c( ddd a)
{
c -= a;
}
}
Compiler Error
a.cs(6,13): error CS0111: Class 'zzz' already defines a member
called 'add_c' with the same parameter types
a.cs(9,13): error CS0111: Class 'zzz' already defines a member
called 'remove_c' with the same parameter types
Each time we create an event
object, two functions are automatically created in our class. These two
functions are the name of the event preface by add_ and remove_. The compiler
adds the code for these function. This also means that we cannot have functions
with the same names in the class that has an event. It could also mean that the
event code overwrites the function code.
What we mean is that in the early
days of C++, the compiler would first convert the C++ statements to C code
which, would then be executed by the C compiler. Maybe something similar is
taking place here too. Our book ‘C# to IL’
handles these issues in great depths.
The functions called by the event
are called event handlers and they provide notifications to our class. The
event like a delegate can be called with a million parameters also.
a.cs
class zzz
{
delegate void ddd();
public static void Main()
{
ddd d = null;
d();
}
}
Output
Unhandled Exception: System.NullReferenceException: Value null
was found where an instance of an object was required.
at zzz.Main()
Whenever we try and execute a
delegate, the system does not perform a compile time check, but at run time it
checks for validity. As we have not yet initialized the delegate d, its value
is null. Hence, we get a NullReferenceException thrown back at us.
a.cs
class zzz
{
public delegate void ddd();
public delegate void eee();
public static void Main()
{
zzz a = new zzz();
ddd d = new ddd(a.abc);
d();
eee e = new eee(d);
e();
System.Type t = typeof(eee);
System.Console.WriteLine(t.FullName);
if ( e is ddd)
System.Console.WriteLine("true");
}
public void abc()
{
System.Console.WriteLine("abc");
}
}
Output
abc
abc
zzz+eee
A delegate constructor cannot be
only passed the name of a function as a parameter as we've done for delegate d,
but also the name of another delegate. Delegate e is an eee delegate but is
initialized to a ddd delegate, d. This simply makes a copy of the original
delegate. We now have two delegate objects that reference the same function,
abc. Internally eee has nothing to do with ddd. The type of eee does not change
due to the copy as copy cannot change
the data type of a delegate.
a.cs
class zzz : yyy
{
public delegate void ddd();
public static void Main()
{
zzz a = new zzz();
ddd d = new ddd(a.abc);
d();
}
}
class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
}
Output
abc
The function being passed to the
delegate is from the base class. This is allowed and we get no apparent error.
a.cs
class zzz : yyy
{
public delegate void ddd();
public void pqr()
{
ddd d = new ddd(base.abc);
d();
}
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void abc()
{
System.Console.WriteLine("abc zzz");
}
}
class yyy
{
public void abc()
{
System.Console.WriteLine("abc yyy");
}
}
Output
abc yyy
We can also use the keyword base
to call the function from the base class. If you remember, base calls code from
the base class and not from the derived class. This is in spite of what the
documentation says and we quote verbatim 'If the method group resulted from a
base-access, an error occurs'. There is no way known to man that can change the
method associated with a delegate once the delegate has been created. It
remains the same for the entire lifetime of the delegate. The parameter to a
delegate creation cannot be a constructor, indexer, property or obviously a
user define operator even though they carry code. We are left with only one
choice as a parameter, a method.
a.cs
class zzz
{
public delegate void ddd();
public static void Main()
{
ddd d = new ddd(zzz);
}
}
Compiler Error
a.cs(6,17): error CS0119: 'zzz' denotes a 'class' which is not
valid in the given context
The error message should have
been far more clearly stating in a loud bold voice, 'Thou shall not give a
Constructor as a parameter to a delegate ' instead of a more generic one which
does not hit the nail on the head.
The
Other Odds and Ends
a.cs
public delegate void ddd();
public class zzz {
public event ddd d1{
add {
return null;
}
}
}
Compiler Error
a.cs(3,18): error CS0065: 'zzz.d1' : event property must have
both add and remove accessors
Whenever we create an event
property, unlike a normal property, an event property must implement both the
add and the remove accessors.
a.cs
public class ddd
{
}
public class zzz
{
public event ddd d1
{
add { return null;}
remove {}
}
}
Compiler Error
a.cs(6,18): error CS0066: 'zzz.d1': event must be of a
delegate type
An event must be the data type of
a delegate and not of a user defined type. Here ddd must only be a delegate
otherwise error no CS0066 results. That is why we first explained a delegate
and then an event. Events follow delegates and thus the designers of C# first
created delegates and as an afterthought events.
a.cs
delegate void ddd();
interface iii {
event ddd d = new ddd();
}
Compiler Error
a.cs(3,11): error CS0068: 'iii.d': event in interface cannot
have initializer
For an interface always follow a
simple rule. We cannot place any code in a interface, including initializing a
variable/object. Interfaces can only have definitions, no code please.
a.cs
public delegate void ddd();
public interface iii
{
event ddd d1 {
remove {}
add {return null;}
}
}
Compiler Error
a.cs(4,11): error CS0069: 'iii.d1': event in interface cannot
have add or remove accessors
In an interface, even a whiff of
code is not allowed. In an accessor we would obviously like to place a lot of
code. In an interface, an event or a property cannot have any accessor code and
hence the {} are bad syntax.
a.cs
public delegate void ddd();
public interface iii
{
event ddd d1
{
remove { }
add { }
}
}
Compiler Error
a.cs(4,11): error CS0069: 'iii.d1': event in interface cannot
have add or remove accessors
Even accessors are not allowed. A
property in an interface behaves a little differently and would not have given
an error in the above case.
Outside the class an event is
declared in, an event can only add or subtract a reference.
a.cs
public delegate void ddd();
public class zzz
{
public event ddd e1;
public static void Main() {
}
}
Compiler Warning
a.cs(4,18): warning CS0067: The event 'zzz.e1' is never used
The compiler considers an event
to be very important so if it is not being used in a program, but declared it
gives you a harmless warning. One more warning number used up. The error and
warning numbers run concurrently. They are no overlaps and share the same
numbers.
a.cs
public delegate void ddd();
interface iii {
event ddd e1;
}
class zzz : iii
{
event ddd iii.e1()
{
}
}
Compiler Error
a.cs(8,14): error CS0071: An explicit interface implementation
of an event must use property syntax
a.cs(8,15): error CS1520: Class, struct, or interface method
must have a return type
Whenever we implement an event
that has been previously declared in an interface, we have to use the property
syntax of a get and set. A normal function syntax will not suffice. Events and
interface work in an odd way together.
a.cs
delegate void ddd (int i);
class zzz {
public static void Main()
{
ddd d = new ddd(12);
}
}
Compiler Error
a.cs(5,17): error CS0149: Method name expected
A delegates only job is to call
another function. The name of the function is handed over to the delegate at
the time of creation of the delegate object. We are passing a number and not a
method name. When calling the function through the delegate only, we pass it an
int, as suggested.
a.cs
public delegate void ddd();
class zzz
{
ddd d;
public static void Main()
{
zzz a = new zzz();
a.d.Invoke();
}
}
Compiler Error
a.cs(8,5): error CS1533: Invoke cannot be called directly on a
delegate
You cannot use the Invoke method to call a delegate directly. The delegate can only be used in one standard way as shown earlier. There is no other way out.