7
Its not what you get but what you give that makes you a
richer person. Unfortunately, this little gem is understood only be a few and
giving remains largely a one way street.
This chapter explains function overloading, the params
parameter and inheritance. We start with function overloading.
Function Overloading
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
a.abc(10);
a.abc("bye");
a.abc("no",100);
}
}
class yyy
{
public void abc(int i)
{
System.Console.WriteLine("abc" + i);
}
public void abc(string i)
{
System.Console.WriteLine("abc" + i);
}
public void abc(string i,int j)
{
System.Console.WriteLine("abc" + i + j);
}
}
Output
abc10
abcbye
abcno100
The class yyy has three functions, all of them having the
same name abc. The distinction between them is in the data types of the
parameters. They are all different. In C# we are allowed to have functions with
the same name, but having different data types parameters. The advantage is
that we call the function by the same name as by passing different parameters,
a different function gets called. This feature is called function overloading.
All is fine only if the parameter types to the function are different. We do
not have to remember a large number of functions by name.
The only reason why function overloading works is that C#
does not know a function by name, but by its signature. A signature denotes the
full name of the function. Thus the name of a function or its signature is the
original function name plus the number and data types of its individual
parameters.
a.cs
class zzz
{
public void abc()
{
}
public int abc()
{
}
public static void
Main()
{
}
}
Compiler Error
a.cs(6,12): error CS0111: Class 'zzz' already defines a
member called 'abc' with the same parameter types
Here we have two functions abc which differ only in the
values they return. As return values do not count in the function signature and
the function names are similar, hence the error.
a.cs
class zzz
{
static void abc(int i)
{
}
public void abc(int i)
{
}
public void abc(string p)
{
}
public static void
Main()
{
}
}
Compiler error
a.cs(6,14): error CS0111: Class 'zzz' already defines a
member called 'abc' with the same parameter types
We have 2 abc's, that accept an int and differ only in the
addition of a modifier static. They have the same signature as modifiers like
static are not considered as part of the function signature. Also, in the next
program, we have two abc's with different access modifiers which differ in the
parameters, hence signature/name changes causing an error.
a.cs
class zzz
{
void abc(int i)
{
}
void abc( out int i)
{
i = 10;
}
void abc( ref int i)
{
}
public static void
Main()
{
}
}
Compiler Error
Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR
version v1.0.2914]
Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
a.cs(10,6): error CS0663: 'abc' cannot define overloaded
methods which differ only on ref and out
a.cs(6,6): (Location of symbol related to previous error)
The signature consists of not only
the parameter data types, but also the kind of parameters i.e. out ref etc. As
function abc takes an int with different modifiers i.e. out etc, the signature
on each is different. The signature of a method consists of its name and number
and types of its formal parameters. The return type of a function is not part
of the signature. No two functions can have the same signature and also
non-members cannot have the same name as members.
A function/method can be called by four different types of
parameters. These are pass by value, reference, output and finally parameter
arrays. The parameter modifier is not part of the function signature. Lets now
understand what parameter arrays are all about.
Params Parameter
A method declaration creates a separate declaration space.
This means that anything created in a method is lost at the end of the method.
a.cs
public class zzz
{
public void abc(int i,string i) {}
public void pqr(int i)
{
string i;
}
public static void Main()
{
}
}
Compiler Error
a.cs(3,23): error CS0100: The parameter name 'i' is a
duplicate
a.cs(6,8): error CS0136: A local variable named 'i' cannot
be declared in this scope because it would give a different meaning to 'i',
which is already used in a 'parent or current' scope to denote something else
Parameter names have to be unique. Also, we cannot have a
parameter and a variable created in a function block with the same name.
In pass by value, the value of the variable is passed. In
the case of ref and out, the address of the reference is passed.
a.cs
public class zzz
{
string s = "hi";
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
abc(ref s,ref s);
System.Console.WriteLine(s);
}
void abc(ref string a, ref string b) {
System.Console.WriteLine(s);
a="no";
System.Console.WriteLine(s);
b = "yes";
System.Console.WriteLine(s);
s = "maybe";
}
}
Output
hi
no
yes
maybe
You are allowed to pass the same ref parameter as many times
as you desire. In the function abc the string s has a value of hi. Then by
changing the string b to no, we are actually changing the string s to no as s
is passed by reference. Variables a and s refer to the same string in memory.
Changing one changes the other. Again changing b also changes s as they refer
to the same string. Thus variables a, b and s refer to the same string in
memory.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
abc(2,"hi","bye","no");
abc(20,"hi");
abc(2);
}
void abc(int i , params string [] b)
{
foreach ( string s in b)
System.Console.WriteLine(s + " " + i);
}
}
Output
hi 2
bye 2
no 2
hi 20
We will encounter a situation where we would like to pass a
variable number of arguments to a function. This is not possible as of now as
C# is extremely finicky about the number and type of data we pass to a
function. If we pass a string where an int is expected, C# starts screaming
like a baby. If we want to pass a variable number of arguments to a function,
we have to use a keyword params. This keyword can only be applied to the last
parameter. Therefore the variable number of arguments can only come at the end.
In the case of function abc, the first parameter has to be an int, the
rest of them can be from zero to an
infinite number of strings.
a.cs
public class zzz
{
public static void Main()
{
}
void abc(int i , params string [] b , int j)
{
}
}
Compiler Error
a.cs(6,18): error CS0231: A params or __arglist parameter
must be the last parameter in a formal parameter list
The params keyword in this version has to be at the end only
as stated above.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
abc(2,3,4);
abc(20,1);
abc(2);
}
void abc(int i , params int [] b)
{
foreach ( int s in b)
System.Console.WriteLine(s + " " + i); } }
Output
3 2
4 2
1 20
C# is smart enough if the penultimate parameter and the
params have the same data type. The first int is stored in the variable i, the
rest are made part of the array b.
a.cs
public class zzz
{
void abc(int i , params string [][] b)
{
}
void abc(int i , params string [,] b)
{
}
}
Compiler Error
a.cs(6,6): error CS0225: The params parameter must be a
single dimensional array
The data type of the params parameter must be, as the error
message states, a single dimensional array. Thus [][] is allowed but not [,].
You are also not allowed to combine the params keyword with ref or out.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
string [] s =
{"hi","bye","no"};
abc(2,s);
}
void abc(int i , params string [] b)
{
foreach ( string s in b)
System.Console.WriteLine(s + " " + i);
}
}
Output
hi 2
bye 2
no 2
You are allowed to pass an array of strings instead of
individual strings as parameters. Here s is an array of strings which has been
initialized using the short form. Internally when we call the function abc, C#
converts the array of strings into individual strings.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
string [] s1 = {"hi","bye"};
abc(2,s1,"hell");
}
void abc(int i , params string [] b)
{
}
}
Compiler Error
a.cs(11,1): error CS1502: The best overloaded method match
for 'zzz.abc(int, params string[])' has some invalid arguments
a.cs(11,7): error CS1503: Argument '2': cannot convert from
'string[]' to 'string'
Mixing and matching is not allowed in C#. What we assumed C#
would do is to add the last string hell to the array of strings s1 or convert
s1 to individual strings and then add the string hell to it. Perfectly logical
we thought. Only if wishes were horses…
Internally before calling the function abc, it collects all
the individual parameters and converts it into one big array for the params
statement.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
int [] a = {1,2,3};
abc(2,a);
System.Console.WriteLine(a[1]);
}
void abc(int i , params int [] b)
{
b[1] = 100;
}
}
Output
100
The output produced is proof of concept. The array member
a[1] has an initial value of 2 and in the function abc we change it to 100. The
original changes, this means that the array is given to the function abc.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
int a = 10;
abc(2,100,a,20);
System.Console.WriteLine(a);
}
void abc(int i , params int [] b)
{
b[1] = 100;
}
}
Output
10
In this case C# creates an array containing 100 10 and 20.
We are changing the second member to 100 which has nothing to do with the
variable a. As abc has no knowledge of a, how on earth can abc change the value
of the int a? Thus it stays the same.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.pqr();
}
void pqr()
{
abc(2);
abc(2,3);
abc(2,3,5,6);
}
void abc(int i, int j)
{
System.Console.WriteLine("two ints "+ i + "
" + j);
}
void abc(params int [] a)
{
System.Console.WriteLine("params a");
}
}
Output
params a
two ints 2 3
params a
Here we are discussing function overloading. C# is extremely
bright though partial. It does not like the params statement and treats it like
a stepchild. When we call abc with one int, C# can only call the abc that takes
a params as a parameter as it matches
one int. An array can contain one member. The fun starts with the abc that is
being called with two ints. Here we have a dilemma. C# can call the params abc
or the abc with two ints. As mentioned earlier, C# treats the params as a
second class citizen and therefore chooses the abc with two ints. When there
are more than two ints like in the third invocation, C# has no choice but to
grudgingly choose the abc with the params. C# chooses the params as a last
resort before flagging an error.
a.cs
using System;
class zzz
{
static void ff(params object[] b) {
foreach (object o in b) {
Console.Write(o.GetType().FullName + " ");
}
Console.WriteLine();
}
static void Main() {
object[] a = {1, "Hello", 123.456};
object o = a;
ff(a);
ff((object)a);
ff(o);
ff((object[])o);
}
}
Output
System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double
In the first case we are passing the function ff an array of
object that looks like object. We will tell you a little later that all classes
are derived from object. The function ff receives an array of objects b. In the
foreach we know that the object class has a function GetType that returns an
object that looks like Type, which in turn has a function called FullName which
returns the name of the type. We see three different types displayed. In the
second invocation of ff we are casting a to an object. There is no conversion
available from converting an object to an object array i.e. object [].
Therefore a one element object [] is created. It's the same case in the third
invocation and the last explicitly casts to an object array.
a.cs
using System;
class zzz
{
static void ff(params object[] b) {
Console.WriteLine(b.GetType().FullName);
Console.WriteLine(b.Length);
Console.WriteLine(b[0]);
}
static void Main() {
object[] a = {1, "Hello", 123.456};
ff((object)a);
}
}
Output
System.Object[]
1
System.Object[]
Inheritance
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a.abc();
}
}
class yyy
{
public int i = 10;
public void abc()
{
System.Console.WriteLine("yyy abc");
}
public void pqr()
{
System.Console.WriteLine("yyy pqr");
}
}
class xxx
{
}
Compiler Error
a.cs(6,1): error CS0117: 'xxx' does not contain a definition
for 'abc'
The class yyy contains 2 functions and one instance
variable. The class xxx contains no code and no variables at all. An empty
class does not denote any error as we are able to instantiate an object that
looks like xxx. The error comes about because the class xxx has no function
called abc. However the class yyy has a function abc. Would it not be
great if we were allowed to use all the
code in the class yyy from xxx. Easier said than done, we guess!
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a.abc();
}
}
class yyy
{
public int i = 10;
public void abc()
{
System.Console.WriteLine("yyy abc");
}
public void pqr()
{
System.Console.WriteLine("yyy pqr");
}
}
class xxx : yyy
{
}
Output
yyy abc
The error disappears and the abc in yyy gets executed. If after the name of the class you write :
yyy i.e. the name of another class, a lot happens at once. xxx is now said to
have been derived from yyy. What that means is all the code we wrote in yyy can
now be used in xxx. It is if we actually wrote all the code that is contained
in yyy in xxx. If we had created an object that looks like yyy, everything that
the object could do, now an object that looks like xxx can also do. But we have
not written a line of code in xxx. We
are made to believe that xxx has one variable i and two functions abc and pqr
as yyy contains these two functions. Here we are teaching you the concepts of
inheritance where yyy will now be called the base class, xxx the derived class.
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a.abc();
}
}
class yyy
{
public int i = 10;
public void abc()
{
System.Console.WriteLine("yyy abc");
}
public void pqr()
{
System.Console.WriteLine("yyy pqr");
}
}
class xxx : yyy
{
public void abc()
{
System.Console.WriteLine("xxx abc");
}
}
Compiler Warning
a.cs(23,13): warning CS0108: The keyword new is required on
'xxx.abc()' because it hides inherited member 'yyy.abc()'
Output
xxx abc
Nothing in the world stops class xxx from creating a
function abc i.e. one with the same name as in the base class . C# simply gives
us a warning. When we run a.abc(), C# first checks whether the class xxx (as a
looks like xxx) has a function called abc. If it does not, then it will check
in the base class. Earlier abc was only available in the base class and hence
got executed. Here as it is already there in xxx, it gets called from xxx and
not yyy. Remember the derived classes get a first shot at execution, then the
base class. The reason being, the base class may have a number of functions and
for various reasons you may not be satisfied with what they do. You should have
the right to have your copy of the function to be called. In other words the
derived classes functions override the ones in the base class.
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a.abc();
}
}
class yyy
{
public int i = 10;
public void abc()
{
System.Console.WriteLine("yyy abc");
}
public void pqr()
{
System.Console.WriteLine("yyy pqr");
}
}
class xxx : yyy
{
public void abc()
{
System.Console.WriteLine("xxx abc");
base.abc();
}
}
Output
xxx abc
yyy abc
What if you want the best of both worlds? You may want to
call the base classes abc first and then yours or vice versa. To accomplish
this, C# gives you a reserved word, something free called base. The word base
can be used in any derived class. It means call the function off the base
class. Simple. Thus base.abc will call the function abc from yyy the base class
of xxx.
a.cs
class zzz
{
public static void Main()
{
xxx a = new xxx();
a.abc();
}
}
class yyy
{
public int i = 10;
public void abc()
{
System.Console.WriteLine("yyy abc");
}
public void pqr()
{
System.Console.WriteLine("yyy pqr");
}
}
class xxx : yyy
{
public void abc()
{
System.Console.WriteLine("xxx abc");
base.pqr();
}
}
Output
xxx abc
yyy pqr
There is only one small change made to the program and that
is base.abc is replaced by base.pqr. In this case the function pqr from the
class yyy gets called. Base is very general purpose. It lets you access members
of the base class from the derived class. You cannot use base in yyy as yyy is
not derived from any class. Thus base can only be used in derived classes.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
a.xyz();
}
}
class yyy
{
public void abc()
{
System.Console.WriteLine("yyy abc");
}
}
class xxx : yyy
{
public void xyz()
{
}
}
Compiler Error
a.cs(6,1): error CS0117: 'yyy' does not contain a definition
for 'xyz'
In this case, xxx is derived from yyy and not vice versa.
Thus xxx can use all the members of yyy. Inheritance does not work backwards.
Whatever members xxx comprises do not permeate upwards to yyy. Class xxx may
now have a function xyz but it cannot give it to class yyy and thus an error
occurs.
A class inherits everything from its base class except the
constructors and destructors. If a class c is derived from class b, which in
turn has been derived from class a, class c inherits all the members declared
in class b and also class a. This concept is called transitive. A derived class
can inherit all the members of the base class but cannot subtract or remove
members off that base class. A derived class can hide members of the base class
by creating functions by the same name. The original member in the base class
remains unchanged and unaffected by whatever is happening in the derived class.
It remains unchanged in the base class, it is simply not visible in the derived
class.
A class member can either be a static member belonging to
the class or an instance member belonging to the instance i.e. accessible
through the object and not the class. The default is non-static.
A class is also called a data structure. It consists of data
members like constants, fields and events and function members like methods,
properties, indexers, operators, constructors, static constructors and
destructors. A class within a class is called a nested class. Thus we can place
11 different types of entities in a class. Function members are the only
members of a class that contain executable code. A class creation creates a new
declaration space.
All classes derive from object . Object is the mother of all
classes.
a.cs
public class zzz : object
{
public static void Main()
{
}
}
If you do not derive from any class, then the C# compiler
automatically adds :object to your class definition. Object, the only class to
have this feature is not derived from any class. It is the ultimate base class
of all classes in the C# hierarchy.
class aa
{
}
class bb : aa
{
}
Class aa is the base class of bb . The documentation however
calls aa the direct base class of bb. Thus the base classes of bb are aa and
object.
a.cs
public class zzz
{
public static void Main()
{
}
}
class aa : System.Delegate
{
}
class bb : System.Enum
{
}
class cc : System.Array
{
}
class dd : System.ValueType
{
}
Compiler Error
a.cs(7,7): error CS0644: 'aa' cannot inherit from special
class 'System.Delegate'
a.cs(10,7): error CS0644: 'bb' cannot inherit from special
class 'System.Enum'
a.cs(13,7): error CS0644: 'cc' cannot inherit from special
class 'System.Array'
a.cs(16,7): error CS0644: 'dd' cannot inherit from special
class 'System.ValueType'
You cannot derive a class from the above 4 classes as they
are special.
a.cs
public class zzz
{
public static void Main()
{
}
}
class aa
{
}
class bb
{
}
class cc : aa, bb
{
}
Compiler Error
a.cs(9,16): error CS0527: 'bb' : type in interface list is
not an interface
A class can only be derived from one more class . You are not permitted to derive
from two or more classes i.e. multiple inheritance is not supported. Thus every
class has one and only one base class.
a.cs
public class zzz
{
public static void Main()
{
}
}
class aa : bb
{
}
class bb : cc
{
}
class cc : aa
{
}
Compiler Error
a.cs(13,7): error CS0146: Circular base class definition
between 'cc' and 'aa'
class aa is derived from bb. Class bb in turn is derived
from cc and cc is derived from aa. This results in a circular definition. class
aa is derived from bb and cc, as bb is derived from cc. As cc is also derived
from aa class, bb also derives from
this class which is aa. Thus aa is derived from aa which is a logical
impossibility.
Equating Objects
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
xxx b = new xxx();
a = b;
b = a;
}
}
class yyy
{
public int i=10;
}
class xxx {
public int j=10;
}
Compiler Error
a.cs(7,5): error CS0029: Cannot implicitly convert type
'xxx' to 'yyy'
a.cs(8,5): error CS0029: Cannot implicitly convert type
'yyy' to 'xxx'
C# has a very simple rule. It does not like you to equate
different objects to each other. Thus an object that looks like yyy cannot be
equated to one that looks like xxx and vice versa. Thus the error. Another
example - you cannot take an int and equate it to a string . C# is extremely
strict when it comes to dealing with different data types.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
xxx b = new xxx();
a = b;
b = a;
}
}
class yyy
{
public int i=10;
}
class xxx : yyy
{
public int j=10;
}
Compiler Error
a.cs(8,5): error CS0029: Cannot implicitly convert type
'yyy' to 'xxx'
There is however one way out. On account of this way, one of
the errors disappeared. The only time we are allowed to equate dissimilar data
types is when we derive from them. Lets explain this in detail.
When we create an instance of yyy by saying new, we are
creating two objects at one go, one that looks like yyy and the other that
looks like object. All classes in C# are finally derived from object. As xxx is
derived from yyy, when we say new xxx, we are creating 3 objects, one that
looks like yyy, one that looks like xxx and finally object.
Thus when we write a = b, b looks like xxx, yyy and object
and as a looks like yyy, there is a match at yyy. Consequence? No error. Even
though a and b have the same values, using a we can only access the members of
yyy, even though had we used b we could access xxx also. We have devalued the
potency of a . The error arises at b = a, because the class yyy is less/smaller
than the class xxx . The class xxx has yyy and more. We cannot have a larger
class on the right and a smaller class on the left. a only represents a yyy
whereas b expects a xxx which is a xxx and yyy. The basic rule is that we can
only equate dissimilar objects if they are derived from each other. You can
equate an object of a base class to a derived class but not vice versa.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
xxx b = new xxx();
a = b;
b = (xxx) a;
}
}
class yyy
{
public int i=10;
}
class xxx : yyy
{
public int j=10;
}
Though we broke a C# rule on equating objects, we did not
get an error because of the cast . A () is called a cast. Within the brackets
we put the name of a class. A cast is the great leveler. When we write b = a,
C# expects the right hand side of the equal to to be a b i.e. a xxx . Instead
it finds a i.e. a yyy . So by applying a cast, we are for the moment converting
the yyy object into an xxx. This strategy satisfies the rules of C# on only
equating similar objects. Remember it is only for the duration of the line that
a becomes a xxx and not a yyy.
a.cs
class zzz
{
public static void Main()
{
yyy a = new yyy();
xxx b = new xxx();
a = (yyy) b;
b = (xxx) a;
}
}
class yyy
{
public int i=10;
}
class xxx
{
public int j=10;
}
Compiler Error
a.cs(7,6): error CS0030: Cannot convert type 'xxx' to 'yyy'
a.cs(8,6): error CS0030: Cannot convert type 'yyy' to 'xxx'
Unfortunately casting works only if one of the two classes
is derived from the other. You cannot
cast any two objects to each other.
a.cs
class zzz
{
public static void Main()
{
int i = 10;
char j = 'A';
i = j;
j = i;
}
}
Compiler Error
a.cs(8,5): error CS0029: Cannot implicitly convert type
'int' to 'char'
We are allowed to convert a char into a int as i = j but not the other way round as j = i.