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.