This is part 4 of the series. Before reading this article, I highly recommend reading the previous parts:
Day 4 Agenda
- Delegates
- Enums
- Attributes
- Generics
Delegates
In a single term we can define a Delegate as type safe pointer to a function. Delegates doesn’t work alone it delegates the associated method to work for it.
Syntax:
publicdelegatevoidCall(); //Simple syntax Non Parametrized Delegate
When you want to use the Class in C# you do in the following ways, you first define the class (properties, methods) and then you instantiate the class using object. With delegate it’s the same process. You first declare the Delegate as shown above i.e. what kind of type will delegate represent. Then, we have to create one or more instances of that delegate. Let’s make a small program defining the definition that Delegate as type safe pointer to a function. We will learn how to declare a Delegate, creating object of Delegate and how to invoke the Delegate.
- using System;
-
- namespace Delegates
-
- {
- public delegate void Dcaller(int Rno, string Ename);
- class Employee
- {
-
- public void PrintInformation(int Rollno, string EmployeeName)
- {
- Console.WriteLine("The Roll no of {0} is {1}", EmployeeName, Rollno);
- Console.ReadLine();
- }
- }
-
-
- class Program {
- static void Main(string[] args)
- {
- Employee obj = new Employee();
- Dcaller delegateObj = new Dcaller(obj.PrintInformation);
- delegateObj.Invoke(1, "Saillesh");
- }
- }
- }
Output: Fig 1.0: Demonstrating Simple delegate
Associating a Delegate with more than One Method (Multicast Delegate).
We can associate our Delegate to more than one method. The association between the delegate and method is created during runtime based on the return type and its signature.
- using System;
- namespace Delegates
- {
- public delegate double Dcalculate(int FirstNum, int SecondNum, int ThirdNum);
-
- classCalculation
- {
- public double Sum(int Num1, int Num2, int Num3)
- {
- return Num1 + Num2 + Num3;
- }
-
-
- public double Multiplication(int Num1, int Num2, int Num3)
- {
- return Num1 * Num2 * Num3;
- }
-
- }
-
-
- classProgram
- {
- staticvoid Main(string[] args)
- {
- double Result;
- Calculation obj = newCalculation();
-
- Dcalculate objDelegate = new Dcalculate(obj.Sum);
- Result = objDelegate.Invoke(10, 20, 30);
- Console.WriteLine("The sum is={0}", Result);
- objDelegate = new Dcalculate(obj.Multiplication);
- Result = objDelegate.Invoke(2, 3, 4);
- Console.WriteLine("The multiplication result is={0}", Result);
- sConsole.ReadLine();
-
-
- }
- }
- }
Output:
Fig 2.0 Demonstrating Multicast delegate
But if focus on the above program I can achieve the same without using the Delegate
Eg1:
- using System;
-
- namespace Delegates
- {
-
- class Employee
- {
-
- public void PrintInformation(int Rollno, string EmployeeName)
- {
- Console.WriteLine("The Roll no of {0} is {1}", EmployeeName, Rollno);
- Console.ReadLine();
- }
- }
-
-
- class Program
- {
- static void Main(string[] args)
- {
- Employee obj = new Employee();
- obj.PrintInformation(1, "Saillesh");
-
- }
- }
- }
Output:
Fig 3.0 Simple calling of a method
Eg 2:
- using System;
-
- namespace Delegates
- {
- class Calculation
- {
- public double Sum(int Num1, int Num2, int Num3)
- {
- return Num1 + Num2 + Num3;
- }
-
-
- public double Multiplication(int Num1, int Num2, int Num3)
- {
- return Num1 * Num2 * Num3;
- }
-
- }
-
-
- class Program
- {
- static void Main(string[] args)
- {
- double Result;
- Calculation obj = new Calculation();
-
- Result = obj.Sum(10, 20, 30);
- Console.WriteLine("The sum is={0}", Result);
- Result = obj.Multiplication(2, 3, 4);
- Console.WriteLine("The multiplication result is={0}", Result);
- Console.ReadLine();
-
-
- }
- }
- }
Output:
Fig 4.0 Simple calling of a method
So we figured it out we can achieve the same without using delegate, so let’s focus what actually does delegate means in common language. Delegates mean communication between two parties.
Fig 5.0 We can see two countries delegate communicating with each other.
Fig 6.0 Demonstration of official Delegate meetings being held in Uttrakhand.
In Windows we have seen Callbacks. Callbacks are method calls that are executed when some event happens during execution.
In computer programming, a callback is executable code that is passed as an argument to other code.
Wikipedia
Eg: You want to delegate the long running operation to a background thread while allowing user interface responsive, However when the execution is running we want to check the status of execution time by time.
Here I am demonstrating small example where I have created a class called Arithmetic with function named Multiplication method which will perform multiplication Table of the range send by user.
Here this function is called via thread which will run at background, while the process is being running with the help of delegate we will be able to check the status and execution steps with the help of callback as shown below.
- using System.Threading;
-
- namespace Shared.cs
- {
- public class del
- {
- public delegate void WhereTocall(int status);
- public WhereTocall wheretocall = null;
- public void Multiplication()
- {
- for (int i = 1; i <= 5; i++)
- {
- for (int j = 1; j <= 10; j++)
- {
-
- int multiply = i * j;
- wheretocall(multiply);
- }
-
- }
-
- }
-
- }
-
- public class Arithmetic
- {
-
- public delegate void DelPointer(string Multiplication);
- public DelPointer delpointer = null;
- public int range
- {
- get;
- set;
- }
- public int result
- {
- get;
- set;
- }
- public string output
- {
- get;
- set;
- }
- public Arithmetic(int upto)
- {
- range = upto;
-
- }
-
- public void Multiplication()
- {
- for (int i = 1; i <= range; i++)
- {
-
- for (int j = 1; j <= 10; j++)
- {
- result = i * j;
- output = i.ToString() + "*" + j.ToString() + "=" + result.ToString();
- delpointer.Invoke(output);
- Thread.Sleep(1000);
- }
-
- }
-
- }
-
-
- }
- }
-
-
- using static System.Console;
- using Shared.cs;
- namespace DELEGATE
- {
-
- delegate void somePointer();
- class Program
- {
- static void Main(string[] args)
- {
- Arithmetic obj = newArithmetic(5);
- obj.delpointer = Print;
- obj.Multiplication();
- ReadLine();
- }
-
- static void Print(string Output)
- {
- WriteLine(Output);
- }
-
-
- }
- }
Fig 7.0 Delegate callback method demonstration.
So by this we can conclude that basically delegates are generally used for communication between two parties i.e. Callbacks and Callbacks.
Enums
Enums are basically strongly typed constants. If the programs contains set of integral values it’s better to replace them with Enum, to make the program more readable and maintainable.
Let consider the following example, we have a class called Order Master which has the following properties.
- public class OrderMaster
- {
- public int OrderId
- {
- get;
- set;
- }
- public string OrderCreatedby
- {
- get;
- set;
- }
- public long MobileNo
- {
- get;
- set;
- }
-
-
-
-
-
-
-
- public int Status
- {
- get;
- set;
- }
- }
Let us take an example when we Order Details to be shared to the User let us try the same:
- class Program
- {
- static void Main(string[] args)
- {
-
-
-
-
- OrderMaster[] order = newOrderMaster[3];
- order[0] = newOrderMaster
- {
- OrderCreatedby = "Saillesh Pawar",
- MobileNo = 999999999,
- OrderId = 001,
- Status = 1
- };
-
- order[1] = newOrderMaster
- {
- OrderCreatedby = "Virender Pawar",
- MobileNo = 999999992,
- OrderId = 003,
- Status = 2
- };
-
-
- order[2] = newOrderMaster
- {
- OrderCreatedby = "Rakesh Pawar",
- MobileNo = 99999993,
- OrderId = 004,
- Status = 5
- };
-
-
-
-
- foreach(OrderMaster orderMaster in order)
- {
- Console.WriteLine($ "Customer Name {orderMaster.OrderCreatedby} OrderId {orderMaster.OrderId}&& MobileNo {orderMaster.MobileNo}&& Status {orderMaster.Status}");
-
- }
- }
- }
Fig 8.0. Demonstration of User Output which seems to be beyond one to understand the Status field.
As we can see the output screen, it doesn’t make sense, as status is 1, 2, and 5. How could user recognize what does 1, 2 and 5 means? So, to make more user friendly we create a function and return the corresponding status value and print it to the user screen.
-
-
- foreach(OrderMaster orderMaster in order)
- {
- Console.WriteLine($ "Customer Name {orderMaster.OrderCreatedby} OrderId {orderMaster.OrderId}&& MobileNo {orderMaster.MobileNo}&& Status {getStatus(orderMaster.Status)}");
-
-
- }
-
-
- public static string getStatus(int id)
- {
-
- switch (id)
- {
-
- case 1:
- return "Ordered Placed";
- case 2:
- return "Order Accepted";
- case 3:
- return "Billed";
- case 4:
- return "Trip Created";
- case 5:
- return "Delivered";
- case 6:
- return "Cancelled";
- default:
- return "Wrong Status";
- }
-
- }
Fig 9.0 Understandable demonstration of User Orders with status but lots of not easily understandable case structure.
Now the output is appropriate for the user as it is showing the status in proper formats. But there are some major concern which make this program unmaintainable. For example, in future more status comes in we need to check into documentation or db to check the values correspond to which status. In these conditions when we have set of integral values, consider replacing them with enum as shown below:
Declaration:
By default they start with 0 and the value of each successive enum is increased by 1 however you can specify the same as per your need.
Problem solving:
-
- public enum Status
- {
-
-
- OrderedPlaced = 1,
- OrderAccepted = 2,
- Billed = 3,
- TripCreated = 4,
- Delivered = 5,
- Cancelled = 6
-
- };
- public class OrderMaster
- {
- public int OrderId
- {
- get;
- set;
- }
- public string OrderCreatedby
- {
- get;
- set;
- }
- public long MobileNo
- {
- get;
- set;
- }
-
- public Status status
- {
- get;
- set;
- }
- }
-
- class Program
- {
-
- static void Main(string[] args)
- {
-
-
-
-
-
- OrderMaster[] order = newOrderMaster[3];
- order[0] = newOrderMaster
- {
- OrderCreatedby = "Saillesh Pawar",
- MobileNo = 999999999,
- OrderId = 001,
-
- status = (Status) 1
- };
-
- order[1] = newOrderMaster
- {
- OrderCreatedby = "Virender Pawar",
- MobileNo = 999999992,
- OrderId = 003,
-
- status = (Status) 2
- };
-
-
- order[2] = newOrderMaster
- {
- OrderCreatedby = "Rakesh Pawar",
- MobileNo = 99999993,
- OrderId = 004,
-
- status = (Status) 5
- };
-
-
-
-
- foreach(OrderMaster orderMaster in order)
- {
- Console.WriteLine($ "Customer Name {orderMaster.OrderCreatedby} OrderId {orderMaster.OrderId}&& MobileNo {orderMaster.MobileNo}&& Status {getStatus(orderMaster.status)}");
-
- }
-
- }
-
-
-
-
-
- public static string getStatus(Status status)
- {
-
- switch (status)
- {
-
-
-
-
- case Status.OrderedPlaced:
- return "Ordered Placed";
- case Status.OrderAccepted:
- return "Order Accepted";
- case Status.Billed:
- return "Billed";
- case Status.TripCreated:
- return "Trip Created";
- case Status.Delivered:
- return "Delivered";
- case Status.Cancelled:
- return "Cancelled";
- default:
- return "Wrong Status";
- }
-
- }
-
- }
Now once we run our program we will find the same output as before but we can see our code is better readable, understandable and easy to maintain if any changes are done.
Fig 10.0 Understandable demonstration of User Orders with status using enum.
Fig 11.0: Difference between case without enum and with enum, enum seems to be more readable as compared without Enum.
Attributes
Attributes are nothing just declarative information which you can attach to your program (i.e. class, property, method) and we can act on it. Attribute is a class which inherits from system.Attribute class. It is denoted by [] square brackets tag.
Let’s take an example of a
Customer class as shown below:
- namespace Attributes
- {
- public class Customer
- {
-
- [Key]
-
- public int CustomerID
- {
- get;
- set;
- }
- public string CustomerName
- {
- get;
- set;
- }
- public string CustomerAddress
- {
- get;
- set;
- }
-
-
-
- public void addEmp(Customer cust)
- {
-
-
- DAL dal = newDAL();
-
-
- dal.customer.Add(cust);
-
-
- dal.SaveChanges();
-
-
- Console.WriteLine("The Customer has been successfully stored in Db with id " + cust.CustomerID);
-
- }
- }
- }
Here in Customer Class we have a method called addEmp() which inserts an Employee to the DB. I am using Entity framework to insert values into the db.
DAL Class- namespace Attributes
- {
- public classDAL: DbContext
- {
-
- public DbSet < Customer > customer
- {
- get;
- set;
- }
-
-
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
-
- modelBuilder.Entity < Customer > ().ToTable("tblCustomerMaster");
- }
- }
- }
So now when user inserts his details the Customer details is saved into DB.
Here,- class Program
- {
- static void Main(string[] args)
- {
-
- Customer obj = newCustomer();
- Console.WriteLine("Enter the UserName");
-
- obj.CustomerName = Console.ReadLine();
- Console.WriteLine("Enter the Address");
- obj.CustomerAddress = Console.ReadLine();
- obj.addEmp(obj);
- Console.ReadLine();
-
- }
- }
Now as the user inserts his details the record is saved into the db as shown below:
Fig 12.0 Demonstrating insertion of Customer Details.
Let’s assume now we want to insert the list of Customer rather than single customer so now we create a new method to insert the list of Customers as shown below:
- public class Customer
- {
-
- [Key]
-
- public int CustomerID
- {
- get;
- set;
- }
- public string CustomerName
- {
- get;
- set;
- }
- public string CustomerAddress
- {
- get;
- set;
- }
-
-
-
- public void addEmp(Customer cust)
- {
-
-
- DAL dal = newDAL();
-
-
- dal.customer.Add(cust);
-
-
- dal.SaveChanges();
-
-
- Console.WriteLine("The Customer has been successfully stored in Db with id " + cust.CustomerID);
-
- }
-
- public static void addEmpList(List < Customer > custList)
- {
-
- DAL dal = newDAL();
- foreach(Customer cust in custList)
- {
-
- dal.customer.Add(cust);
- }
- dal.SaveChanges();
-
-
- if (dal.SaveChanges() == 0)
- {
-
- List < Customer > lstCus = dal.customer.ToList < Customer > ();
- foreach(Customer customer in lstCus)
- {
-
- Console.WriteLine($ "Customer name:{customer.CustomerName} with Id:{customer.CustomerID} lives in {customer.CustomerAddress}");
-
- }
-
- }
-
- }
-
- }
-
-
- class Program
- {
- static void Main(string[] args)
- {
-
- List < Customer > objCustList = newList < Customer > ();
-
- Console.WriteLine("Enter the list of customers to be added");
-
- int range = Convert.ToInt32(Console.ReadLine());
-
- for (int i = 0; i < range; i++)
- {
-
- Customer customer = newCustomer();
- Console.WriteLine("Enter the UserName");
-
- customer.CustomerName = Console.ReadLine();
- Console.WriteLine("Enter the Address");
- customer.CustomerAddress = Console.ReadLine();
-
- objCustList.Add(customer);
- }
- Customer.addEmpList(objCustList);
- Console.ReadLine();
-
- }
- }
Now run the program and we will see the following output, as shown below:
Fig 13.0 Records after trying new method for adding list of Customers to the db.
Now we can see we have two methods namely:
- addEmpList //for adding list of Customer to the database
- addEmp //for adding a customer to the database
As we can see in the code that addEmp is an outdated function because it can handle only one Customer while in addEmpList it can handle up to n Customer. So we want users to use our addEmpList method rather than addEmp method. So, I want to restrict the user in order not to user addEmp method, so here where Attribute comes into picture. As definition says attribute is a declarative information which you can specify to your programs.
Eg:
- [Obsolete]
- o now I tag Obsolete on the top of my addEmp method
-
- [Obsolete]
- public static void addEmp(Customer cust)
- {
-
-
- DAL dal = newDAL();
-
-
- dal.customer.Add(cust);
-
-
- dal.SaveChanges();
-
-
- Console.WriteLine("The Customer has been successfully stored in Db with id " + cust.CustomerID);
-
- }
Now if I try to call this method we can see the message is deprecated.
Fig 14.0. Demonstrating the deprecated message to the user so that he can understand this method has been removed or old.
Fig 15.0 You need to replace this with new function
If we try to use it still, it will show a warning. What if we want to restrict the user from using and want to send him alternative workarounds for this method?
Fig 16.0 Demonstration of Constructor in Obsolete attribute.
There are three overloaded attribute of Obsolete as shown above. Let us see the same:
- public ObsoleteAttribute()
- public ObsoleteAttribute(string message);
- public ObsoleteAttribute(string message, bool error);
- error:
-
-
-
- [Obsolete("Use only addEmpList method ")]
- public static void addEmp(Customer cust)
- {
-
-
- DAL dal = newDAL();
-
-
- dal.customer.Add(cust);
-
-
- dal.SaveChanges();
-
-
- Console.WriteLine("The Customer has been successfully stored in Db with id " + cust.CustomerID);
-
- }
Now if the user tries to call the addEmp method he will see a message denoting
"Use only addEmpList method” as shown below:
Fig 17.0 Showing the Developer which method to use.
Now restricting user to use it by showing error we will use 3 overload of attribute and use error true:
- [Obsolete("Use only addEmpList method ", true)]
- public static void addEmp(Customer cust)
- {
-
-
- DAL dal = newDAL();
-
-
- dal.customer.Add(cust);
-
-
- dal.SaveChanges();
-
-
- Console.WriteLine("The Customer has been successfully stored in Db with id " + cust.CustomerID);
-
- }
Fig 18.0 Not allowing the developer if he/she tries to use the obsolete function.
There are various attributes that are already defined in .net.
Some of them as shown below:
- [Web Method]: to expose method over a Web Service.
- [Key] in entity Framework:Denotes one or more properties that uniquely identify an entity.
- [Required]: validation for property not to be null.
- [MaxLength(10)] validation for property whose max length should be 10.
Generics
Generic was added to 2.0 framework, it is the concept of separating logic from your data type which means we can delay the declaration of our data type in our class or methods until it is being called by the client.
Let us take a scenario what we generally mean by Generics.
We want to have a simple compare function which compares two integer value as shown below which returns a Boolean value true or false for the same.
- using System;
- using static System.Console;
- namespace sIMPLE
- {
- class Program
- {
- static void Main(string[] args)
- {
-
- bool result = CompareClass.Compare(10, 20);
- Console.WriteLine(result);
- ReadLine();
- }
- }
-
-
- public class CompareClass
- {
-
-
- public static bool Compare(int num1, int num2)
- {
- return num1 == num2;
- }
-
- }
- }
What if we want to do the same for string values i.e. we need to create the same method which accepts string as parameter as shown below:
- class Program
- {
- static void Main(string[] args)
- {
-
- bool result = CompareClass.Compare("saillesh", "saillesh");
- Console.WriteLine($ "The result of the compare method is {result}");
- ReadLine();
- }
- }
-
-
- public class CompareClass
- {
-
-
- public static bool Compare(string str1, string str2)
- {
- return str1 == str2;
- }
- }
Fig 19.0: Demonstration of compare function on string values.
What if we want our method to accept separate our Compare data type the declaration of our data type to be anything i.e. it can be int, string, double etc.
How about trying it by using object parameters (as are base class is object).
- class Program
- {
- static void Main(string[] args)
- {
-
- bool result = CompareClass.Compare("saillesh", 1);
- Console.WriteLine($ "The result of the compare method is {result}");
- ReadLine();
- }
- }
-
-
- public class CompareClass
- {
-
-
- public static bool Compare(object str1, object str2)
- {
- return str1 == str2;
- }
-
- }
Fig 20.0 Demonstration of object type compare which results in poor performance due to boxing and unboxing.
We may however be able to compare the object types but it’s seems foolish to allow numbers and string compared to each other as we did above. Apart from that we are performing boxing and unboxing which may result in poor performance because when a value type is boxed it is creating a new object which takes a long time as compared to simple reference.
So in order to avoid boxing and unboxing, In order to take the strongly typed data type Generics comes into picture which can delay the declaration of data type to be strongly typed in our class or method until it’s called by the client (or during the runtime).
Declaration of using a Generic type in C#.
public static bool Compare<T>(T str1,T str2)Here <T> is declaration defining a Generic Type it can be anything as per your naming conventions.
But when client want to consume this method he needs to declare or define the type as shown below:
bool result = CompareClass.Compare<int>(1, 1);
This simply implies that we want to compare two integer values.
bool result = CompareClass.Compare<string>("Saillesh", "pawar");
Compare two string values.
But in our compare function we will not be able to perform == so in place of that we will do,
return str1.Equals(str2);Which will return the true or false. Now with the help of using Generic we can separate our logic from the data type.
If we try to use define the int type data type and try to pass string in one parameter we face compile time error.
Fig 21.0: Demonstrating strongly typed data types are accepted when we use generic of type int.
We can specify the type on the class level also as shown below:
- class Program
- {
- static void Main(string[] args)
- {
-
- bool result = CompareClass < int > .Compare(1, 2);
- Console.WriteLine($ "The result of the int compare method is {result}");
-
- result = CompareClass < string > .Compare("Saillesh", "Pawar");
- Console.WriteLine($ "The result of the string compare method is {result}");
- ReadLine();
- }
- }
-
-
- public class CompareClass < T >
- {
-
-
- public static bool Compare(T str1, T str2)
- {
- return str1.Equals(str2);
- }
- }
So, here we learned how we can decouple our logic from the data type with the help of Generic.