Friday, April 12, 2019

C# Function Overloading and Inheritance

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.

No comments:

Post a Comment

No String Argument Constructor/Factory Method to Deserialize From String Value

  In this short article, we will cover in-depth the   JsonMappingException: no String-argument constructor/factory method to deserialize fro...