C# Operators
C# Operators
. Operator
The dot operator (.) is used for member access.
...
But what about having a class with the same name in multiple namespaces?
2|Page
namespace Sample. Second
{
public class Bar { }
}
namespace Demo
{
using Sample. First;
using Sample. Second;
class Program
{
public static void Main()
{
Bar b = new Bar();
}
}
}
namespace Demo
{
using A = Sample. First;
using B = Sample. Second;
class Program
{
public static void Main()
{
A.Bar b = new A.Bar();
}
}
}
using System;
using System.Collections.Generic;
using IntList = System.Collections.Generic.List<int>;
using List = System.Collections.ArrayList;
class Program
{
public static void Main()
{
IntList lst = new IntList();
3|Page
Console.WriteLine(lst.GetType());
List lstc = new List();
Console.WriteLine(lstc.GetType());
List<int> lstg = new List<int>();
Console.WriteLine(lstg.GetType());
}
}
we talk about Dynamic Link Libraries (DLLs). Some people also called them C# class libraries.
Step 1:
Open your Visual Studio 2015 preview, go to the file menu, go to the new, go with the
project, from the template field select C#, select Class Library then name it as you want. Save it at an
appropriate place. So, from the browse button you can select the appropriate location for saving the
DLL.
Step 2:
After creating this Class Library (DLL) in the Solution Explorer you will see the class. In this
class there is no method and no property at the initial level. This class is totally empty. We will now
add to this class.
4|Page
Step 3:
As I said, there is one class that was generated named Class1. When we double-click on this
class then we see the namespace rizDLL. We use this namespace for the further implementation, in
other words when we create our Windows based program we use this namespace in our program.
When you double-click on the class file you will see something like this.
using System;
using System.Text;
using System.Threading.Tasks;
namespace rizDLL
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class Class1
{
public Class1() // this is the constructor of class1
{
// Add constructor logic here
}
}
}
Step 4:
After doing this we will check whether everything in our project is okay. So, for checking this
we simply go to the tab menu then select build and click on it.
5|Page
Step5:
When you click on the build solution then in the output window you will see a message that
the build is successful.
Now go to the location where you saved your rizDLL program. Open it, go to the bin folder, go to the
debug folder in the debug folder you will see the DLL file named as you chose when creating the
project. Here my DLL File name is rizDLL.dll.
Step 6:
Now for adding the method and properties in the class file we simply go to the Solution
Explorer then right-click on it and click on view Class Diagram.
When you click you have seen a Class diagram and in that diagram you will see an empty class.
Step 7:
Now here we add a method to our class. For adding the method to our class, we simply right-
click on the class diagram and then we have an option, Add. Click on Add, now there are many
options such as Add Method, Property, Field, Events and many more options.
6|Page
When we click on the Add method then there is a method will Add to our class. Now in the
bottom of our Visual Studio we have options to change the property of this method. Here we add two
methods, one method name is mcTestMethod. In mcTestMethod we do not use any parameters.
Another method name is Add Method, this method has three numbers and returns the sum of those
numbers.
The overall process adds two methods to our class file and the methods look as in the given code.
Step 8:
Now we add a property to our class file. To add the property to our class file we simply follow
the same procedure we followed to create the method. Simply go to the class diagram then right-
click on it, click on Add, then click on the property. Here we add a property and name it Extra and
typed it bool and used the public modifier with the get and the set properties.
Step 9:
After Adding the property with the set and get method, add the following code to the class
file. Here I change my class and constructor name, both for convenient implementation. Here we
make the class name and constructor name usedll.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
7|Page
namespace rizDLL
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class usedll
{
private bool bTest = false;
public usedll()
{
//
// TODO: Add constructor logic here
//
}
/// <summary>
/// //This is a test method
/// </summary>
public void mcTestMethod()
{
}
/// <summary>
/// //This is a test property
/// </summary>
public bool Extra
{
get
{
return bTest;
}
set
{
bTest = Extra;
}
}
Step 10:
After adding the code successfully, we again build our solution and have a message in the
output window that you have successfully built it. Now you can see your DLL code in your bin/debug
folder.
8|Page
Add DLL file in to the Another Program
Now I will explain how to use the rizDLL in your project. Use the following procedure.
Step 1:
Again, open Visual Studio 2015 Preview, go to the file menu, go to the project, add a new
project then select the template for Visual C# then add a new console application.
Step 2:
After that add the references for the library. So, for this we go to the tab menu of Visual
Studio then go to the tab project then go to the Add Reference command.
9|Page
Step 3:
When you click on Add References you will see a window like this.
Now simply click on the browse button and add the DLL file from the specific position where you
saved it.
Step 4:
After adding the DLL file in your console application, please open the Solution Explorer of
your console application. Explore the references in the references. You will see that DLL file.
10 | P a g e
Step 5:
Now open the class file of the Console Application and add the namespace "using rizDLL".
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rizDLL;
namespace usingrizDll
{
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
usedll cls = new usedll();// object of dll class
long lRes = cls.Add(23, 40,30);
cls.Extra = false;
Console.WriteLine("The sum of your three nos is=" +lRes.ToString());
Console.Read();
}
}
}
Step 6:
11 | P a g e
Now build your Console Application and Run it. After a successful build you will see the
output as in the following picture.
Output:
Many users know how to create code library using Visual Studio IDE in GUI environment for
making code reusable code so that multiple application can use that dll. But here I am writing
something by which after reading this article you will be able to compile DLL using just command line
C# compiler without opening Visual Studio IDE.
Language:
C# .net 2.0/3.5
Prerequisites:
Implementation
First let you tell what our target is to compile a DLL (reusable code library) using command
line c# compiler that is csc.exe.
As example we will build simple dll that will contain methods like add() , substract()
multiply().
using System;
12 | P a g e
namespace SimpleMaths
{
public class Operations
{
public static int add(int a, int b)
{
return a + b;
}
public static int substract(int a, int b)
{
return a - b;
}
public static int multiply(int a, int b)
{
return a * b;
}
}
}
Save the file with <FileName>.cs
13 | P a g e
Press Enter and you will get your desired DLL file generated!!
You just have created DLL file with Command Line C# Compiler. Now use that dll file in any .net
project !!
Now you will raise question how do I compile multiple source file (.cs) files?
Well simple just execute command like below one pass 2 names of source files in command
while compiling it.
Example:
// File: Add.cs
namespace UtilityMethods
{
public class AddClass
{
public static long Add (long i, long j)
{
return (i + j);
}
}
}
// File: Mult.cs
namespace UtilityMethods
{
public class MultiplyClass
{
public static long Multiply (long x, long y)
{
return (x * y);
}
}
}
// File: TestCode.cs
using UtilityMethods;
class TestCode
{
static void Main (string [] args)
{
System. Console. WriteLine ("Calling methods from MathLibrary.DLL:");
if (args. Length! = 2)
{
System. Console. WriteLine ("Usage: TestCode <num1> <num2>");
return;
14 | P a g e
}
long num1 = long. Parse (args [0]);
long num2 = long. Parse (args [1]);
long sum = AddClass.Add(num1, num2);
long product = MultiplyClass.Multiply(num1, num2);
System. Console. WriteLine ("{0} + {1} = {2}", num1, num2, sum);
System. Console. WriteLine ("{0} * {1} = {2}", num1, num2, product);
}
}
/* Output (assuming 1234 and 5678 are entered as command-line arguments):
Calling methods from MathLibrary.DLL:
1234 + 5678 = 6912
1234 * 5678 = 7006652
*/
15 | P a g e
The /reference argument is only used to indicate the name of the assembly.
To specify additional directories to search for assembly files use the /lib argument:
csc *.cs /r:Newtonsoft.Json.dll /lib:"./dir with spaces/need quotes", ./bin/Debug
Use /lib to specify the directory in which one or more of your assembly references is located.
The /lib topic also discusses the directories in which the compiler searches for assemblies.
you should use hyphen (-) instead of front slash
csc -lib:"D:/C# Projects/Lib1/Lib1/bin/Debug" -r:Lib1.dll Program.cs
The other option is
csc -r:"D:/C# Projects/Lib1/Lib1/bin/Debug/Lib1.dll" Program.cs
csc /r:lib1.dll,lib2.dll program.cs
For runtime, you need those referenced dlls in the same folder of your current application.
So, copy those dlls and put it in your current directory and run it.
Library 2 (lib2.dll)
namespace Bar
{
public class Def { }
public class Foo { }
}
Is this valid to do? Yes of course, since we define a class (coincidentally or not) with the same
name "Bar. Foo" in two different assemblies there is no conflict. If you'd compile it into one library
the CS0101 error would pop up (telling you have two identical definitions). However, when you start
both libraries in one single application, problems appear:
using Bar;
class Program
{
16 | P a g e
public static void Main()
{
Foo f = new Foo();
}
}
Using the following command-line compiler invocation:
error CS0433: The type 'Bar. Foo' exists in both 'c:\temp\lib1.dll' and 'c:\temp\lib2.dll'
There is however a solution to this: extern aliases. Change the program's code as follows:
using Lib1::Bar;
class Program
{
public static void Main()
{
Foo f1 = new Foo();
Lib2::Bar.Foo f2 = new Lib2::Bar.Foo();
}
}
or
extern alias Lib1;
extern alias Lib2;
17 | P a g e
using Bar2 = Lib1::Bar;
class Program
{
public static void Main()
{
Bar1.Foo f1 = new Bar1.Foo();
Bar2.Foo f2 = new Bar2.Foo();
}
}
Compilation should be performed like this:
18 | P a g e
{
static void Main()
{
// Syntax variant #1
AnotherLib::DogLibrary.Dog d2 = new
AnotherLib::DogLibrary.Dog("JRT", "Jack");
// Syntax variant #2
AnotherLib.DogLibrary.Dog d3 = new
AnotherLib.DogLibrary.Dog("Rough Collie", "Lassie");
}
}
Scope Resolution Operator (::):
The scope resolution operator :: is used to identify and disambiguate identifiers used in
different scopes.
in C++ the scope resolution operator i.e. :: is used for global variables, whereas in C# it is
related to namespaces.
If you have a type that share an identifier in different namespace, then to identify them use
the scope resolution operator.
For example, to reference System.Console class, use the global namespace alias with the
scope resolution operator
global::System.Console
using myAlias = System.Collections;
namespace Program {
class Demo {
static void Main() {
myAlias::Hashtable h = new myAlias::Hashtable();
h.Add("M", "1");
h.Add("N", "2");
h.Add("O", "3");
h.Add("P", "4");
foreach (string n in h.Keys) {
global::System.Console.WriteLine(n + " " + h[n]);
}
}
}
}
Remarks
The namespace alias qualifier can be global. This invokes a lookup in the global namespace,
rather than an aliased namespace.
The ability to access a member in the global namespace is useful when the
member might be hidden by another entity of the same name.
19 | P a g e
Using System;
class TestApp
{
// Define a new class called 'System' to cause problems.
public class System { }
However, you can work around this error by using global::System.Console, like this:
global::System.Console.WriteLine(number);
Obviously, creating your own namespaces called System is not recommended, and it is
unlikely you will encounter any code in which this has happened. However, in larger projects,
it is a very real possibility that namespace duplication may occur in one form or another. In
these situations, the global namespace qualifier is your guarantee that you can specify the
root namespace.
Using System.Collections;
Hashtable obj=new Hashtable(); //NO ERRORS FOUND
System.Collections.Hashtable test = new System.Collections.Hashtable(); //ERROR: The type
name “Collections” does not exist in the type " TestApp.System”
global::System.Collections.Hashtable test = new global::System.Collections.Hashtable(); //OK
Syntax
:: identifier
class-name :: identifier
namespace :: identifier
enum class :: identifier
20 | P a g e
enum struct :: identifier
Remarks:
The identifier can be a variable, a function, or an enumeration value.
namespace App
{
class System
{
System ()
{ }
static void Main (string [] args)
{
global:: System.Console. WriteLine ("Amit");
global:: System.Console. ReadKey (true);
}
}
}
21 | P a g e
using System;
using NS1;
Using MyNS=NS1;
using MyNS = NS1; //alias name.
namespace NS1
{
public class MyClass
{
public void Display ()
{
Console.WriteLine("Display () Method of MyClass in NS1");
}
}
}
public class MyClass
{
public void Display ()
{
Console.WriteLine("Display () Method of MyClass in Global Namespace");
}
}
class UsingMyClass
{
public static void Main ()
{
MyNS::MyClass myObject = new MyNS::MyClass ();
myObject. Display ();
global::MyClass yourObject = new global::MyClass ();
yourObject. Display ();
(OR)
MyClass obj=new MyClass();
Obj.Display();
}
}
Output:
Display () Method of MyClass in NS1
Display () Method of MyClass in Global Namespace
Display () Method of MyClass in Global Namespace
22 | P a g e
Type Conversion in Expressions:
6/4=1
6/4.0=1.5
6/(float)4=1.5;
23 | P a g e
6.0/4=1.5
int/int int
int/float float
int/double double
int/char int
Float/int float
10/0 Error
0/0 Error
2. For non-zero floating numbers divisible by zero, will get Infinity (+ve or -ve).
10/0.0 Infinity
-10/0.0 - Infinity
10/-0.0 - Infinity
10.34/0 Infinity
3. For Zero floating numbers divisible by Zero, will get NaN.
0/0.0 NaN
0.0/0 NaN
0.0/0.0 NaN
-0/0.0 NaN
0/10 0
0/10.2 0
0.0/10 0
% Operator:
The modulus operator % returns the remainder of a division operator.
********The sign of the remainder is always sign of the numerator only.
10%3 1
-10%3 -1
10%-3 1
12.3%-3.2 2.7
-12.3%-3.2 -2.7
1. Integer number modulus by Zero, will get an error.
10%0 Error
0%0 Error
2. For floating numbers modulus by zero, will get NaN.
10%0.0 NaN
-10%0.0 NaN
10.0%0 NaN
0.0%0 NaN
24 | P a g e
0%0.0 NaN
0.0%0.0 Nan
0%10 0
0%10.2 0
0.0%10 0
Relational Operators:
< , > , <= ,>=
The result type is always Boolean, and we can apply for the primitive types except Boolean.
10 > 20 //False
10.0>20.0 //False
97>’A’ //True
Remember
10=10.0f=10.0D
10>20>40 //error
Equality Operators:
== !=
We can apply equality operators for both primitives and object references.
10==10.0f //True
‘a’==97 //True
true==true //True
true!=false //True
10.25f==10.25D //False
true!=10 //Error
25 | P a g e
In case of Object references r1,r2
r1==r2 returns true if both r1 and r2 pointing to the same object on heap.
Thread t1=new Thread();
Thread t3=t1;
t1==t2 //false
t1==t3 //true
In order to use == or != operators, both object references must be of the same type.
Thread t1=new Thread();
String s1=”hello”;
t1==s1 //error
t1!=s1 //error.
t1==obj //false
t1!=obj //true
Bitwise operators:
&, |,^ can be applicable for both Boolean and integer type.(not in floating type)
~ can be applicable for integer type only. (~x = -(x+1)). (not in floating type)
Char ch=’A’;
Int i=~ch; o/p: -66
Int i=~’A’; o/p: -66
Runtime:
byte a=10;
int c=~a; o/p: -11
byte b=(byte) ~a; o/p: 245 (unexpected value)
byte b=~a; o/p: Error required byte, found int
sbyte b=(sbyte)~a; o/p: -11
Compile Time:
const byte a=10;
int c=~a; o/p: -11
byte b=(byte) ~a; o/p: ERROR (byte not allow -ve values)
26 | P a g e
byte b=~a; o/p: ERROR (byte not allow -ve values)
sbyte b=~a; o/p: -11
casting is not required because of execution done at compile time.
Compile time expression evaluate and check the result value fit with result type or not.
Logical Operators:
&& ||
Applicable only for Boolean type.
X && Y X || Y
Here the evaluation of y is optional if it is compulsory then only ‘y’ will be evaluated.
X || Y -> if x is false then only y will be evaluated.
X && Y -> if x is true then only y will be evaluated.
Difference between Bitwise and logical operators:
X&Y X|Y X&&Y X||Y
Both x and y should always be evaluated The evaluation of y is optional. If it is
compulsory then only y will be evaluated.
Applicable for both integer type and Boolean Only Boolean type
type
Assignment Operators:
Single assignment operators: (=)
Int i=’A’;//ok
Ushort s=’B’;//ok
Short s1=’Z’;//Error
double x;
int i;
i = 5; // int to int assignment
x = i; // implicit conversion from int to double
i = (int)x; // needs cast
Console.WriteLine("i is {0}, x is {1}", i, x);
object obj = i;
Console.WriteLine("boxed value = {0}, type is {1}", obj, obj.GetType());
i = (int)obj;
Console.WriteLine("unboxed: {0}", i);
Output:
i is 5, x is 5
boxed value = 5, type is System.Int32
27 | P a g e
unboxed: 5
Compound assignment operators:
+=,-=,*=,/=,%= <<=,>>=,>>>=,&=,|=,^=
Chained assignment operators:
Int a, b, c, d;
a=b=c=d=100; //ok
int a, b, c, d, a=b=c=d=100;//error
Int a,b;
int a=b=c=d=100; //error.
a=10 , b=20; //error
a=10;b=20; //OK
Int a, b, c, d;
int a=10,b=20; //ok
a=b=c=d=20;
a+=b-=c*=d/=2;
WriteLine (a+” “+b+” “+c+” “+d);
d=d/2=20/2=10;
Int a,b,c;
c=c*d=20*10=200;
Int d=3,e=4,f=5;
b=b-c=20-200=-180;
Int a, byte b;//error
a=a+b=20+(-180)=-160
o/p: -160 -180 200 10
28 | P a g e
At compile time direct assignment is done with the following steps.
1. Check base on result type, the values are within the group type range or not.
Int a=10;
Const byte b=a; //CE: Expression being assigned to b must be constant.
//THIS TYPE OF ASSIGNMENT IS NOT ALLOWED IN C#.Because Constant values
assignment done at compile time.
Runtime initialisation:
int i=10;
byte j=i; // i should be of same type.
Console.WriteLine(j);// error CS0266: Cannot implicitly convert type `int' to `byte'.
Examples
Const byte b1=100;
Const sbyte b2=100;
Const short b3=100;
Const ushort b4=100;
Const int b5=100;
Byte c=b1; //ok . because same type
Byte c=b2; //error.
Byte c=b3; //error
Byte c=b4; //error
29 | P a g e
Byte c=b5; //ok. Because int type.
Conditional operators: ?:
int x =(10>20)?10:20;
Constant Expression:
Constant expression evaluated at compile time;
1. The compiler first checks the values of range based on group type.
2. Check based on result type, the two variables are with in the group type or not.
byte b= (10>20)?122:20; //OK
byte b=(10>20)?1222L:20;//Error
byte b=(10>20)?10.2:20;//Error
3. Evaluate the expression, after evaluation, we get true or false value. Based on this value, we
check true or false block, value is fitted with result type or not.
byte b=10>20?127:128;//Error
byte b=10<20?127:128;//Ok
byte b=10.2<20.7?127:128;//Ok
Variable Expression:
Variable expression evaluated at Runtime.
byte b=x>y ? val1: val2;
1. When val1,val2 are constant values. i.e. directly given the value
byte b=x>y ? 10: 20;
a. Compiler first checks the values of ranges based on group type.
b. The compiler takes the integer values as int type without specify any literal type.
byte b=i>j?10:20;//10,20 as int type
2. When val1,val2 are variables.
a. val1,val2 should be result type compulsory.
byte b=(byte)((10>20)?122:20); //ok 20
int a=10;
int b=20;
byte v=(a>b)?122:20;//error :error CS0266: Cannot implicitly convert type `int' to `byte'.
int a=10;
int b=20;
byte c=100;
byte v=(a>b)?a:c; //error here a and c both are promoted to higher datatype int.
error CS0266: Cannot implicitly convert type `int' to `byte'.
int a=10;
int b=20;
30 | P a g e
byte c=100;
byte v=(a>b)?c:c; //ok both are of result type.
Example
class Program
31 | P a g e
{
static void Main(string[] args)
{
//EXAMPLE 01 :
int? x = null;
// y = x, unless x is null, in which case y = -1
int y = x ?? -1;
//EXAMPLE 02 : ?? Operator with Value Type Assign i to return value of
method, unless return value is null, in which case assign default value of int to i
int i = GetNullableInt() ?? default(int);
//EXAMPLE 03 : ?? Operator with Reference Type
string s = GetStringValue();
// Assign content of s to z, unless s is null, in which case assign "Unspecified"
var z = s ?? "Unspecified";
}
static int? GetNullableInt()
{
return null;
}
static string GetStringValue()
{
return null;
}
}
Output:
Example 01
Example 02
Example 03
int? a=null;
int b=a; // Compile Time: error CS0266: Cannot implicitly convert type `int?' to `int'.
int? a=null;
int b=a. Value; //Runtime Error: System. InvalidOperationException: Nullable object must have a value.
int? a=10;
int b=a; // Compile Time: error CS0266: Cannot implicitly convert type `int?' to `int'.
int? a=10;
int b=a. Value; (or) int b=(int)a;
32 | P a g e
Usage of ?? Operator
The chaining is a big plus for the ?? operator. It removes a bunch of redundant IFs
The C# Null Coalescing Operator (??) is a binary operator that simplifies checking for
null values. It can be used with both nullable types and reference types. It is represented as x
?? y which means if x is non-null, evaluate to x; otherwise, y.
x is the first operand which is a variable of a nullable type.
y is the second operand which has a non-nullable value.
While executing code, if x evaluates to null, y is returned as the value. Also remember that
the null coalescing operator (??) is right-associative which means it is evaluated from right to
left. So, if you have an expression of the form x ?? y ?? z, this expression is evaluated as x??
(y?? z).
Now with an overview of the C# Null Coalescing operator, let us see how to use the Null
coalescing operators in practical scenarios.
Scenario 1 – Assign a Nullable type to Non-Nullable Type
Consider the following piece of code where we are assigning a nullable type to a non-
nullable type.
In the code above, if the nullable type (‘a’) has a null value and is assigned to a non-nullable
type ( ‘b’), an exception of type InvalidOperationException is thrown, as shown below:
33 | P a g e
One way to resolve this error is to use IF..ELSE condition and check the value of the nullable
type before assigning it to the non-nullable type
The code will now compile and give you desired results. However, using the null coalescing
operator in such scenarios, you can create clearer code than the equivalent if-else statement,
as shown below:
In the code shown above, if ‘a’ has been assigned a non-null value, then this value
will be assigned to the int b. However, since the nullable type ‘a’ has been assigned null, the
value to the right of the operator (??) i.e. zero will be assigned to b instead.
The value of b if printed, is 0.
byte? a=10;
sbyte b=a??0; // Cannot implicitly convert type 'byte' to 'sbyte'.
Scenario 2 – Initializing instance fields in a Struct
You cannot declare instance field initializers in a Struct. The following piece of code:
34 | P a g e
However, using the Null coalescing operator, you can use a work-around as follows:
The value A can now be accessed and will return 1. Quite a handy technique using the
Nullable type and the Null Coalescing operator!
There are some more ways the Null-coalescing operator can be used. I have covered some
basic uses in my article Different Ways of using the C# Null Coalescing Operator.
Here’s a simple example of using the Null-Coalescing operator
int? i = null;
int j = i ?? 2;
// here the resultant value of j is 2
Here are some additional ways of using the Null-Coalescing operator in your code.
Method 1
string address = homeAddress1 ?? homeAddress2 ?? officeAddress ?? "No Address";
returns the first non-null value in this long expression. This expression returns “No
Address” if homeAddress1, homeAddress2 and officeAddress are Null.
Method 2
While converting a Nullable Type to Non-Nullable type, we do an explicit cast to avoid
assigning null values to a non-nullable type, as shown below:
int? a = NullType(); // returns nullable int
int b = (int)a;
instead you can avoid the explicit cast using a null-coalescing operator and write:
int? a = NullType();
int b = a ?? 0;
Method 3
private IList<Person> person;
public IList<Person> PersonList
{
get
{
return person ?? (person = new List<Person>());
}
}
Typical usage of the null-coalescing operator in a lazy instantiated private variable.
35 | P a g e
Method 4
If I use the NULL conditional operator (?.), then the preceding code snippet can be written as
given below.
static void Main(string[] args)
{
string name = null;
int? length = name?. Length;
}
It will run successfully without throwing any exception.
36 | P a g e
NULL conditional operator for member access (?.) with null-coalescing operator (??)
if you do not want to make the length variable as Nullable int type, then you can use null-
coalescing operator (??) with “?.” Follow the code snippet given below for the same.
static void Main(string[] args)
{
string name = null;
int length = name?. Length ?? 0;
}
37 | P a g e
If I use the NULL conditional operator (?[), then the preceding code snippet can be written, as
given below.
static void Main(string[] args)
{
int[] Numbers = { 1, 2, 3, 4, 5 };
Numbers = null;
WriteLine($"5th element is: {Numbers?[4]}");
}
It will run successfully without throwing any exception.
In previous versions null checking for any kind of objects manually we need to write if
condition then we need to write our required properties or else we need to write our business
logic.
If(Object != null)
{
If(Object. User != null)
{
return object.User.Name;
} Else
{
return null;
}
}
Another way of checking null condition using ternary operator (Single line if statement) as
follows:
return object!=null? (object. User!=null?object.User.Name:null):null;
Now In c# 6.0 Microsoft is coming up with a new null-conditional operator that helps you
write these checks more sufficiently as follows
return object?. User?. Name;
Note: If object is null then it won’t navigate to nested object.
Before we explain this feature, let's see why we need this feature. Assume we have a class
with two properties.
38 | P a g e
public class UserData
{
public String Username { get; set; }
public String Password { get; set; }
public String Token { get; set; }
}
We create an instance of this class but do not assign any value to the Token property. So, it
will be NULL. So, in our code, when we use these properties, we add the NULL check for the
Token property.
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
UserData _userData = new UserData();
_userData.Username = "User1";
_userData.Password = "TestPassword";
if (_userData.Token == null)
Console. Write ("Token not provided");
else
Console. Write ("Token Initialized");
Console.ReadKey();
}
}
public class UserData
{
public String Username { get; set; }
public String Password { get; set; }
public String Token { get; set; }
}
}
Look's good and works fine. With C# 6.0, this NULL check process is made more simplified. In
order to check any value, we can simply use the convention:
instanceName?.PropertyName
Confused? Let's replace the preceding convention with the code instanceName => userData
and PropertyName => Token. So, we change our code to C# 6.0 and check the NULL using the
preceding convention. So, our code becomes:
using static System.Console;
namespace NullOperator
{
class Program
39 | P a g e
{
static void Main(string[] args)
{
UserData _userData = new UserData();
_userData.Username = "User1";
_userData.Password = "TestPassword";
Console. Write (_userData?.Token?? “Token Not Initialised”);
Console.ReadKey();
}
}
public class UserData
{
public String Username { get; set; }
public String Password { get; set; }
public String Token { get; set; }
}
}
So, what we did is, we simply add "?" to check the value of the Token and write it to
the Console, if it is available else do nothing. Let's add the binary operator to print the message
if no token is available. So, we simply add the message as:
Console. Write (_userData?.Token ?? "Token not provided");
using System;
class Program
{
static string _name;
/// Property with custom value when null.
static string Name
{
get {return _name ?? "Default";}
set {_name = value;}
}
static void Main()
{
Console.WriteLine(Name);
Name = "Perls";
Console.WriteLine(Name);
Name = null;
Console.WriteLine(Name);
}
}
Output:
Default
Perls
Default
40 | P a g e
string userName = "Banketeshvar Narayan";
userName = null;
if ((username?. Length??0)>20)
WriteLine("UserName cannot be more than 20-character long.");
else
WriteLine("UserName length is: {0}", username?. Length??0);
IS / AS OPERATORS:
Look at the example given below:
Circle c = new Circle (32);
object o = c;
int i = (int)o;// it compiles okay but throws an exception at runtime
41 | P a g e
Here the runtime is more suspicious; if the type of object in memory does not match the cast,
the runtime will throw an InvalidCastException.
IS Operator
IS Operator is used to Check the Compatibility of an Object with a given Type and it
returns the result as a Boolean i.e. True or False. True if given object is compatible with new
one, else false.
Example:
using System;
class Class1
{
}
class Class2
{
}
public class IsTest
{
public static void Test (object o)
{
Class1 a;
Class2 b;
if (o is Class1)
{
Console.WriteLine("o is Class1");
a = (Class1) o;
}
else if (o is Class2)
{
Console.WriteLine("o is Class2");
b = (Class2) o;
}
else
Console.WriteLine("o is neither Class1 nor Class2.");
}
public static void Main ()
{
Class1 c1 = new Class1();
Class2 c2 = new Class2();
Test(c1);
Test(c2);
Test ("Passing String Value instead of class");
}
}
42 | P a g e
Object obj = new Object (); // Creates a new Object obj
// checking compatibility of obj object with other type
Boolean b1 = (obj is Object); // b1 is set to true.
Boolean b2 = (obj is Employee); // The cast fails: no exception is thrown, but b2 is set to false.
//we can also use it
if (obj is Employee)
{
Employee emp = (Employee) obj; // TO DO:
}
In above, CLR is checking the obj object type twice. First time with in the if condition and if it
is true, with in the if block. This way affects the performance since every time CLR will walk the
inheritance hierarchy, checking each base type against the specified type (Employee). To avoid this,
we have AS operator.
If the reference of the given object is null, the IS operator will return false since there is no object
available to check its type.
Object obj=null;
bool b=obj is Object;
Console.WriteLine(b); //False
AS Operator
As Operator is used for Casting of Object to a given Type. It returns non-null if given
object is compatible with new one, else null. In this way AS operator help you to do safe type
casting. The above code can be re-written by using AS operator in a better way.
43 | P a g e
If the reference of the given object is null, the AS operator will return NULL since there is no object
available to check its type.
In this way, CLR is checking the obj object type only one time. Hence AS operator provide good
performance over IS operator. Now if you want to use emp object then it will throw
NullReferenceException as given below.
class Class1{
}
class Class2{
}
public class IsTest
{
public static void Main ()
{
object [] myObjects = new object [6];
myObjects [0] = new Class1();
myObjects [1] = new Class2();
myObjects [2] = "string";
myObjects [3] = 32;
myObjects [4] = null;
for (int i = 0; i < myObjects.Length; ++i)
{
string s = myObjects[i] as string;
Console. Write ("{0}:", i);
if (s! = null)
Console.WriteLine("'" + s + "'");
else
Console.WriteLine("not a string");
}
Console.ReadKey();
}
}
44 | P a g e
Expression Explanation
checked(a) uses overflow checking on value a
unchecked(a) avoids overflow checking on value a
new operator:
We can divide the use of the "new" keyword into the following uses:
An operator
A modifier
A constraint
new as an operator
It is used to create objects and invoke a constructor.
Using the "new" operator, we can create an object or instantiate an object, in other words
with the "new" operator we can invoke a constructor.
45 | P a g e
var authors = new List<Author>();
Create an instance of anonymous types:
//anonymous can also be initialized by new
var authorAnonymous = from auth in authors
select new { FullName = string. Format ("{0} {1}", auth.FirstName, auth.LastName) };
An important thing to notice is that with the use of the "new" operator we can invoke the
default constructor of a value type.
Int num = new int(); //think about this
Interesting point
It will throw an exception if it fails to allocate memory. Think of a scenario where it does fail.
Declaring a default constructor for a struct results in an error. The reason is that every value
type implicitly invokes its default constructor.
Value-types objects, like int, are created on the stack and reference type objects like "Author"
are created on the heap.
There will be an error if you use both "new" and "override" on the same member.
46 | P a g e
The following is wrong:
47 | P a g e
BaseAuthor.Author = new BaseAuthor.Author();
"new" as a Constraint
It specifies that the generic type must have a public parameter less constructor. The "new"
constraint cannot be used on an abstract type.
Simple example
If a method Test() is declared in the base class A and classes B or C has no methods as
shown below.
using System;
class A
{
public void Test() { Console.WriteLine("A::Test()"); }
}
class B : A { }
class C : B { }
class Program
{
static void Main(string[] args)
{
A a = new A();
48 | P a g e
a.Test(); // output --> "A::Test()"
B b = new B();
b.Test(); // output --> "A::Test()"
C c = new C();
c.Test(); // output --> "A::Test()"
}
}
Output: No errors and No Warranting’s
A::Test()
A::Test()
A::Test()
Remember:
class Bike{
void run(){System.out.println("running");}
}
class Splender extends Bike{
void run(){System.out.println("running safely with 60km");}
public static void main(String args[]){
Bike b = new Splender();//upcasting
b.run();
}
}
Output: running safely with 60km.
Suppose you have Test () method in all the classes A, B, C as shown below:
using System;
class A
{
public void Test() { Console.WriteLine("A::Test()"); }
}
class B : A
{
public void Test() { Console.WriteLine("B::Test()"); }
}
class C : B
{
public void Test() { Console.WriteLine("C::Test()"); }
}
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
49 | P a g e
C c = new C();
a.Test(); // output --> "A::Test()"
b.Test(); // output --> "B::Test()"
c.Test(); // output --> "C::Test()"
a = new B();
a.Test(); // output --> "A::Test()"
b = new C();
b.Test(); // output --> "B::Test()"
}
}
When you will run the above program, it will run successfully and gives the O/P. But this program will
show the two warnings as shown below:
'B.Test()' hides inherited member 'A.Test()'. Use the new keyword if hiding was intended.
C.Test()' hides inherited member 'B.Test()'. Use the new keyword if hiding was intended.
The method should be `virtual` in the base class if you want child classes to be able to
override it. The warning you're getting is indicating that, the base class is not virtual, you
cannot override it, you can only shadow it. By applying the `new` modifier you aren't
changing any behaviour, you're just indicating that the method is shadowing, not overriding,
the base class. Since you don't want to shadow it, you want to override it, you need to
modify the base class to make the method virtual. The warning can't specify that as the
solution though, as in many cases the user won't have the ability to change the base class
definition.
using System;
class A
{
public void Test() { Console.WriteLine("A::Test()"); }
}
class B : A
{
public new void Test() { Console.WriteLine("B::Test()"); }
50 | P a g e
}
class C : B
{
public new void Test() { Console.WriteLine("C::Test()"); }
}
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
C c = new C();
a = new B();
a.Test(); // output --> "A::Test()"
b = new C();
b.Test(); // output --> "B::Test()"
}
}
Moreover, if you are expecting the fourth and fifth output should be "B:: Test ()" and "C::
Test ()" since the objects a and b are referenced by the object of B and C respectively then
you must re-write the above code for Method Overriding.
new keyword denotes that we define a new method with the parent class name.
Parent can hold the reference to its child, but Child cannot hold the reference to
its parent.
51 | P a g e
Virtual and Overridden Methods
Only if a method is declared virtual, derived classes can override this method if they
are explicitly declared to override the virtual base class method with the override keyword.
class A
{
public virtual void Foo() { Console.WriteLine("A::Foo()"); }
}
class B : A
{
public override void Foo() { Console.WriteLine("B::Foo()"); }
}
class Test
{
static void Main(string[] args)
{
A a;
B b;
a = new A();
b = new B();
a.Foo(); // output --> "A::Foo()"
52 | P a g e
b.Foo(); // output --> "B::Foo()"
a = new B();
a.Foo(); // output --> "B::Foo()"
}
}
}
Combining Method Overriding and Hiding
Methods of a derived class can both be virtual and at the same time hide the derived
method. In order to declare such a method, both keywords virtual and new have to be used
in the method declaration:
class A
{
public void Foo() {}
}
class B : A
{
public virtual new void Foo() {}
}
A class C can now declare a method Foo() that either overrides or hides Foo() from class B:
class C : B
{
public override void Foo() {}
// or
public new void Foo() {}
}
Conclusion
C# is not Java.
Only methods in base classes need not override or hide derived methods. All methods
in derived classes require to be either defined as new or as override.
Know what you’re doing and look out for compiler warnings.
Important Points About Keywords Virtual and Override
In C#, if you like to override the parent class method then you must mark the parent
method by keyword “Virtual” and method in derived class which intended to override the
parent method should be marked by keyword “override”.
Note:
If parent method is marked by keyword “Virtual” but child is not marked by keyword
“override”, then program will compile but the parent method will not be override. A
warning came. (CS0114: `Child.drive()' hides inherited member `Parent.drive()'.)
53 | P a g e
If Child method is marked by keyword “override” but parent method is not marked by
keyword “virtual” then program will not compile. It will give following error:
‘TestB. display ()’: cannot override inherited member ‘TestA. display ()’ because it is not marked
virtual, abstract, or override.
This article explains the main differences among overriding, hiding and shadowing in C#.
I am mainly focusing on the main points and not detailed descriptions. So, you will finish this
very quickly. Also, some examples to understand the concepts better are provided.
1. The "override" modifier extends the base class method, and the "new" modifier hides
it.
2. The "virtual" keyword modifies a method, property, indexer, or event declared in the
base class and allows it to be overridden in the derived class.
3. The "override" keyword extends or modifies a virtual/abstract method, property,
indexer, or event of the base class into the derived class.
4. The "new" keyword is used to hide a method, property, indexer, or event of the base
class into the derived class.
5. If a method is not overriding the derived method then it is hiding it. A hiding method
must be declared using the new keyword.
6. Shadowing is another commonly used term for hiding. The C# specification only uses
"hiding" but either is acceptable. Shadowing is a VB concept.
What are the differences between method hiding and overriding in C#?
54 | P a g e
1. For hiding the base class method from derived class simply declare the derived class
method with the new keyword.
Whereas in C#, for overriding the base class method in a derived class, you need to
declare the base class method as virtual and the derived class method as overridden.
2. If a method is simply hidden then the implementation to call is based on the compile-
time type of the argument "this".
Whereas if a method is overridden then the implementation to be called is based on
the run-time type of the argument "this".
3. New is reference-type specific, overriding is object-type specific.
What are the differences between method hiding and method shadowing?
1. Shadowing is a VB concept. In C#, this concept is called hiding.
2. The two terms mean the same in C#.
Method hiding == shadowing
3. In short, name "hiding" in C# (new modifier) is called shadowing in VB.NET (keyword
Shadows).
4. In C# parlance, when you say "hiding" you're usually talking about inheritance, where
a more derived method "hides" a base-class method from the normal inherited
method call chain.
5. When you say "shadow" you're usually talking about scope; an identifier in an inner
scope is "shadowing" an identifier at a higher scope.
6. In other languages, what is called "hiding" in C# is sometimes called "shadowing" as
well.
55 | P a g e
Method hiding with warning
56 | P a g e
Example 3: Overriding, Hiding and Shadowing
57 | P a g e
58 | P a g e
Example 4 : Method Overriding and Method Hiding:
59 | P a g e
Upcasting or Generalization or Widening:
Converting from differ types (individual) to common type.
Placing sub class memory into super class reference variable.
Downcasting or Specialization or Narrowing
Converting from common type to individual type.
Placing sub class memory into sub class reference variable by using cast operator,
which is pointing by superclass reference.
Class A{
Int i=10;
Non-Static Variables
}
Static methods
Class B extends A{ Using obj Static Variables
Int m=20; you can
} access
Class Test{ supper
Public void Main(){ class
A obj=new B();//Upcasting
Console.WriteLine(obj.i);//10
Console.WriteLine(obj.m);// error not accessible
B obj2=(B) obj; //Downcasting
Console.WriteLine(obj2.m);// 20
}
} Using obj Non-Static methods
you can
access in
sub class
60 | P a g e