Monday, September 7, 2020

Generics

  • Generic classes and methods combine reusability, type safety, and efficiency in a way that their non-generic counterparts cannot.
  • Generics introduce the concept of type parameters to .NET, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.
  • For example, by using a generic type parameter T, you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations, as shown here:
  •     public class GenericList<T>
        {
            public void Print(T key)
            {
                Console.WriteLine(key);
            }
    
        }
    	
       class TestGenericList
       {
        static void Main()
        {
           // Type of T is int
            var genericnumbers = new GenericList<int> ();
            genericnumbers.Print(5);
    
    	// Type of T is strings
            var genericStrings = new GenericList<strings>();
            genericStrings.Print("Janaki");
        }
    }
    	
    
  • The System.Collections.Generic namespace contains several generic-based collection classes.We can also create custom generic types and methods to provide your own generalized solutions and design patterns that are type-safe and efficient.
Constraints on Type Parameters

  • Constraints specify the capabilities and expectations of a type parameter.
  • Without any constraints, the type argument could be any type. The compiler can only assume the members of System.Object, which is the ultimate base class for any .NET type.
  • If client code uses a type that doesn't satisfy a constraint, the compiler issues an error. 
  • Constraints are specified by using the where contextual keyword. Below are some examples. 
    • where T : struct -  The type argument must be a non-nullable value type. 
    • where T : class -  T must be a non-nullable reference type.
    • where T: <any base class> :  T must be a non-nullable reference type derived from the specified base class.  
    •     public class CalculatePrice<T> where TProduct : Product
          {
              public decimal getPrice(TProduct product)
              {
                  return product.price;
              }
          }
          public class Product
          {
              public decimal price { get; set; }
          }
      	
      
    • where T : <interface name> - The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.  
    • where T : new() - T is an object type that has a default constructor. 
    •     public class CalculatePrice<T> where TProduct : new()
          {
              public void DoSomeThing()
              {
                  var Onj = new T();
              }
           }
      

  • Type parameters that have no constraints can not use !=, > ,== operators because there's no guarantee that the concrete type argument will support these operators.
  •     public class Utilities<T> where T :IComparable
        {
            public int Max(int a, int b)
            {
                return a > b ? a : b;
            }
    
            public T Max (T a, T b)
            {
                //Can not compare by using operators a>b
                return a.CompareTo(b)>0 ? a : b;
            }
    
        }
    	
    
References :  docs.microsoft.com and udemy.com

Thursday, September 3, 2020

Interfaces

  • Interface contains declaration of methods, properties, events and indexers. 
  • public interface ITaxCalculator
    {
     int Calculate();
    }
    
  • Members of an interface do not have access modifiers and can not declare variables in the interface because fields are about the implementation details. 
  • Interfaces help building loosely coupled applications. We reduce the coupling between two classes by putting an interface between them. This way, if one of these classes changes, it will have no impact on the class that is dependent on that (as long as the interface is kept the same).
Interface and Testability
  • In order to unit test a class, we need to isolate it. This means: we need to assume that every other class in our application is working properly and see if the class under test is working as expected. But in below example, OrderProcessor class is depended on the ShippingCalculator class because of "CalculateShipping" method in "Process" method.
  •     public class Shipment
        {
            public DateTime shippingdate;
            public object cost { get; set; }
        }
    
        public class Order
        {
            public Shipment Shipment;
            public bool IsShipped { get { return Shipment != null; }}
            public DateTime DatePlaced { get; set; }
            public float TotalProce { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var orderprocessor = new OrderProcessor();
                var order = new Order { DatePlaced = DateTime.Now, TotalProce = 100f };
                orderprocessor.Process(order); 
            }
        }
    	
        public class OrderProcessor
         {
            private readonly ShippingCalculator _shippingCalculator;
    
            public OrderProcessor()
            {
                _shippingCalculator = new ShippingCalculator();
            }
    
            public void Process(Order order)
            {
                if (order.IsShipped)
                {
                    throw new InvalidOperationException("The order already is shipped");
                }
    
                order.Shipment = new Shipment
                {
    		//Depend on the ShippingCalculator class
                    cost = _shippingCalculator.CalculateShipping(order),
                    shippingdate = DateTime.Now.AddDays(1)
                };
            }
        }
    	
        public class ShippingCalculator
        {
            public float CalculateShipping(Order order)
            {
                if (order.TotalProce > 30f)
                {
                    return order.TotalProce * 0.01f;
                }
                return 0;
            }
        }
    	
    
  • A class that has tight dependencies to other classes cannot be isolated. To solve this problem, we should use an interface. (The changes are in bold)
  •   public class Shipment
        {
            public DateTime shippingdate;
            public object cost { get; set; }
        }
    
        public class Order
        {
            public Shipment Shipment;
            public bool IsShipped { get { return Shipment != null; }}
            public DateTime DatePlaced { get; set; }
            public float TotalProce { get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var orderprocessor = new OrderProcessor(new ShippingCalculator());
                var order = new Order { DatePlaced = DateTime.Now, TotalProce = 100f };
                orderprocessor.Process(order);
                Console.Read();  
            }
        }
    	
        public class OrderProcessor
        {
            private readonly IShippingCalculator _shippingCalculator;
    
            public OrderProcessor(IShippingCalculator calculator)
            {
                _shippingCalculator = calculator;
            }
    
            public void Process(Order order)
            {
                if (order.IsShipped)
                {
                    throw new InvalidOperationException("The order already is shipped");
                }
    
                order.Shipment = new Shipment
                {
                    cost = _shippingCalculator.CalculateShipping(order),
                    shippingdate = DateTime.Now.AddDays(1)
                };
            }
        }
    	
        public interface IShippingCalculator
        {
            float CalculateShipping(Order order);
        }
    	
        public class ShippingCalculator : IShippingCalculator
        {
            public float CalculateShipping(Order order)
            {
                if (order.TotalProce > 30f)
                {
                    return order.TotalProce * 0.01f;
                }
                return 0;
            }
        }
Interfaces and Extensibility
  • We can use interfaces to change our application’s behaviour by “extending” its code(rather than changing the existing code).
  • If a class is dependent on an interface, we can supply a different implementation of that interface at runtime. This way, the behaviour of the application changes without any impact on that class.
  • For example, let’s assume our DbMigrator class is dependent on an ILoggerinterface. At runtime, we can supply a ConsoleLogger to log the messages on the console. Later, we may decide to log the messages in a file (or a database). We can simply create a new class that implements the ILogger interface and inject it into DbMigrator.
  • 
        public interface ILogger
        {
            void LogInfo(string message);
            void LogError(string message);
        }
        
    	public class DBMigrator
        {
            private readonly ILogger _logger;
    
            public DBMigrator(ILogger logger)
            {
                _logger = logger;
            }
    
            public void Migrate()
            {
                _logger.LogError("DBMigrating started at " + DateTime.Now);
                _logger.LogInfo("DBMigrating ended at " + DateTime.Now);
            }
        }
    
        public class ConsoleLogger : ILogger
        {
            public void LogError(string message)
            {
                Console.WriteLine(message);
            }
    
            public void LogInfo(string message)
            {
                Console.WriteLine(message);
            }
        }
    
    
        public class FileLogger : ILogger
        {
            public void LogError(string message)
            {
                //Here should write the code to insert the log error in to file
                Console.WriteLine(message);
            }
    
            public void LogInfo(string message)
            {
                //Here should write the code to insert the loginfo in to file
                Console.WriteLine(message);
            }
        }
    	
    
References : Udemy.com

Wednesday, September 2, 2020

Sealed Classes and Members

  • If "sealed" modifier applied to a class, prevents derivation from that class.
  • If "sealed" modifier applied to a method, prevents overriding of that method in a derived class.  
  • Sealed classes are slightly faster because of some run time optimization.  But sealed classes are hardly ever used. 
  • The string class is declared as sealed, and that’s why we cannot inherit from it


Abstract Classes and Members

  •  Abstract modifier used to indicate that a class or member is missing the implementation. 
  • The purpose of having a abstract class to provides a common definition of a base class that multiple derived classes can be shared.
  • We use abstract members when it doesn’t make sense to implement them in a base class. For example, the concept of drawing a shape is too abstract. We don’t know how to draw a shape. This needs to be implemented in the derived classes.
  •     public class Shape
        {
            public virtual void Draw()
            {
            }
        }
    
        public class Circle : Shape
        {
            public override void Draw()
            {
                Console.WriteLine("Drawing a circle");
            }
        }
    
    
  • In these situations, better approach is to use abstract modifier. In a derived class, we need to override all abstract members of the base class, otherwise that derived class is going to be abstract too.  
  •     public abstract class Shape
        {
            public abstract int Weight { get; set; }
            public abstract void Draw();
    		
            //Concrete Method
            public void Print()
            {
            }
    
        public class Circle : Shape
        {
            private int _weight;
            public override int Weight
            {
                get
                {
                    return _weight;
                }
    
                set
                {
                    Weight = _weight;
                }
            }
    
            public override void Draw()
            {
                Console.WriteLine("Drawing a circle");
            }
        }
    
  • When a class member is declared as abstract, that class needs to be declared as abstract as well. That means that class is not complete.
  • We can not define abstract variables and abstract class cannot be instantiated. 
  • In C#, "Stream" class is an abstract class and because it is too abstract. But FileStream is derived from System.IO.Stream and it is very specific. It allows to read and write data to file. 



Tuesday, September 1, 2020

Polymorphism

 Polymorphism means ability to have many forms. There are two types pf polymorphism. 

  1. Compile time polymorphism/Overloading
  2. Runtime polymorphism/Overriding
Overloading

  • Compile time polymorphism is method and operators overloading. It is also called early binding.
  • In method overloading method performs the different task at the different type and different no of input parameters with the same method name.
  • You should consider overloading a method when you need a couple of methods for some reason that take different parameters, but conceptually do the same thing.
  •     class Program  
        {  
            public class Shape  
            {  
                public void Area(float r)  
                {  
                    float a = (float)3.14 * r;  
                    // Function overload with 1 parameter.  
                    Console.WriteLine("Area of a circle: {0}",a);  
                }  
                public void Area(float l, float b)  
                {  
                    float x = (float)l* b;  
                    // Function overload with 2 parameters.  
    Console.WriteLine("Area of a rectangle: {0}",x); } public void Area(float a, float b, float c) { float s = (float)(a*b*c)/2;
    // Function overload with 3 parameters.
    Console.WriteLine("Area of a circle: {0}", s); } } static void Main(string[] args) { Shape shape= new Shape(); shape.Area(2.0f);
    shape.Area(20.0f,30.0f);
    shape.Area(2.0f,3.0f,4.0f);
    Console.ReadLine(); } }

Overriding
  • Method overriding means changing the implementation of an inherited method.
  • If a declare a method as virtual in the base class, we can override it in a derived class.
  • At run-time, when client code calls the method, the CLR looks up the run-time type of the object, and invokes that override of the virtual method. 
  • In the source code you can call a method on a base class, and cause a derived class's version of the method to be executed.
  • Virtual methods enable you to work with groups of related objects in a uniform way. For example, suppose you have a drawing application that enables a user to create various kinds of shapes on a drawing surface. Even though this can be  achieved by no of switch statements, it will be hard to maintenance. So you can achieve the same thing by using overriding a method.
  •     public class Shape
        {
            public int X { get; private set; }
            public int Y { get; private set; }
            public int Height { get; set; }
            public int Width { get; set; }
    
            // Virtual method
            public virtual void Draw()
            {
                Console.WriteLine("Performing base class drawing tasks");
            }
        }
    
        public class Circle : Shape
        {
            //Override Method
            public override void Draw()
            {
                // Code to draw a circle...
                Console.WriteLine("Drawing a circle");
            }
        }
        public class Rectangle : Shape
        {
            public override void Draw()
            {
                // Code to draw a rectangle...
                Console.WriteLine("Drawing a rectangle");
            }
        }
        public class Triangle : Shape
        {
            public override void Draw()
            {
                // Code to draw a triangle...
                Console.WriteLine("Drawing a triangle");
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                var shapes = new List
                            {
                             new Rectangle(),
                             new Triangle(),
                             new Circle()
                            };
    
                // invoked Draw() on each of the derived classes, not the base class.
                foreach (var shape in shapes)
                {
                    shape.Draw();
                }
                Console.Read();
            }
        }
        
        Output
        Drawing a rectangle
        Drawing a triangle
        Drawing a circle
    	
    
  • A derived class is able to stop virtual inheritance by declaring an override member as "sealed".
  •     public class Triangle : Shape
        {
            public sealed void Draw()
            {
                // Code to draw a triangle...
                Console.WriteLine("Drawing a triangle");
            }
        }
    
  • Using the "base" keyword, the derived class is able to access the method.
  •     public class Triangle : Shape
        {
            public override void Draw()
            {
    	   base.Draw(); 
            }
        }

Saturday, August 29, 2020

Reference Types and Value Types

Value Types

  • Value type are the variables which are stored in its own memory location.
  • Value types are stored in stack according to the order it is created.
Only c will be changed to 7.


Reference Types 

  • Reference types does not stored its value within its own memory location. Instead of that , it stores a pointer/address/reference where the actual value is stored.
  • Reference types are stored in heap. But the variable is stored in stack and data is stored on the heap. 

Both d.hp and f.hp will be changed to 100.

 



Tuesday, August 25, 2020

Upcasting and Downcasting

  •  Upcasting: conversion from a derived class to a base class
  •  All objects can be implicitly converted to a base class reference.  
  •       Shape shape = circle;   // Shape is base class and circle is derived class	
    
  • Downcasting: conversion from a base class to a derived class 
  • Downcasting requires a cast.
  •       Circle circle= circle;   // Shape is base class and circle is derived class	
  • Casting can throw an exception if the conversion is not successful. We can use the as keyword to prevent this. If conversion is not successful, null is returned. 
  •       Circle circle = shape as Circle;
          if (circle != null)
  • We can also use the is keyword
  • if (shape is Circle) {
    var circle = (Circle) shape;
    }