Friday, September 11, 2020

Lambda Expression

  • It is an anonymous method which has no access modifier, no name and  no return statement. 
(input-parameters) => expression
  • We can write lambda expression to achieve something with less code. Below example shows how we can write a code to get multiplication of two numbers with lambda expression and without lambda expression. 
  •     class Program
        {
            static void Main(string[] args)
            {
                //args => expression
    
                //Without Lambda Expression
                Console.WriteLine(Square(5));
    
                //With Lambda Expression and Func
                Func<int, int> multiplication = number => number * number;
                Console.WriteLine(multiplication(5));
    
                Console.Read();
            }
    		
    	private static int Square(int x)
            {
                return x * x;
            }
        }
    	
    
  • We can also use lambda expressions when we write LINQ in C#.
  • int[] numbers = { 2, 3, 4, 5 };
    var squaredNumbers = numbers.Select(x => x * x);
    Console.WriteLine(string.Join(" ", squaredNumbers));
    // Output:
    // 4 9 16 25
    
    //Consider new BookRepository().GetBooks() returns list of books
    var listofBooks = new BookRepository().GetBooks();
    List<Books> cheapBooks = listofBooks.FindAll(b => b.price < 20).ToList();
    	
    

Thursday, September 10, 2020

Delegates

  • Delegate is an object that knows how to call a method or a  group of methods which have the same signature. 
  • It is a reference to a function.
  • Of course, we can call methods directly, but we need delegates for designing extensible and flexible applications. (Ex: Frameworks).
  • Imagine that you are designing a framework which is use for processing the photos. In that framework, you have defined few photo processing ways such as "Apply Brightness" , "Apply Contrast" and etc. In future, may be a client who is using your framework need to add their own photo filter which we haven't defined. So in that case, we have to change our application , recompile and redeploy. It is not a good idea. If we use delegates in our frameworks, client can add any no of filters without relying on us. See below example. 
  •     public class Photo
        {
            public Photo Load(string path)
            {
                return new Photo();
            }
        }
    	
        public class Filter
        {
            public void Resize(Photo photo)
            {
                Console.WriteLine("Changed the Size");
            }
    
            public void ChangeBrightness(Photo photo)
            {
                Console.WriteLine("Changed the brightness");
            }
    
            public void ChangeContrast(Photo photo)
            {
                Console.WriteLine("Changed contrast of the photo");
            }
    
        }
    	
        public class PhotoMaker
        {
            // Define the delegate
            public delegate void PhotoFilterHandler(Photo photo);
            public void Process(string path, PhotoFilterHandler photofilterhandler )
            {
                var photoObject = new Photo();
                var photo = photoObject.Load(path);
                photofilterhandler(photo);
            }
        }
    	
        class Program
        {
            static void Main(string[] args)
            {
                var photomaker = new PhotoMaker();
                var filter = new Filter();
    			
    	    // Adding ChangeBrightness filter
                PhotoMaker.PhotoFilterHandler filterhandler = filter.ChangeBrightness;
    			
    	    //Adding ChangeContrast filter
    	    filterhandler += filter.ChangeContrast;
    			
    	    // Remove ChangeBrightness filter
                filterhandler -= filter.ChangeBrightness;
    			
    	    //Adding custom method  without changing the PhotoProcess class
                filterhandler+= RemoveRedEye;
               
    	    photomaker.Process("", filterhandler);	
    
                Console.Read();
            }
    
            static void RemoveRedEye(Photo photo)
            {
                Console.WriteLine("Remove Red Eye");
            }
        }
    	
    
  • In .Net, there are two delegates which are generic. They are Action and Func. Difference between Func and Action is Func point to a method that has a return value and Action point to a method that returns void (no return type). 

  • Below code uses in built Action delegate instead of creating a custom delegate. 
  •  	
    public class Photo
        {
            public Photo Load(string path)
            {
                return new Photo();
            }
        }
    	
        public class Filter
        {
            public void Resize(Photo photo)
            {
                Console.WriteLine("Changed the Size");
            }
    
            public void ChangeBrightness(Photo photo)
            {
                Console.WriteLine("Changed the brightness");
            }
    
            public void ChangeContrast(Photo photo)
            {
                Console.WriteLine("Changed contrast of the photo");
            }
        }
    	
        public class PhotoMaker
        {
            public void Process(string path, Action<Photo> photofilterhandler )
            {
                var photoObject = new Photo();
                var photo = photoObject.Load(path);
                photofilterhandler(photo);
            }
        }
    	
        class Program
        {
            static void Main(string[] args)
            {
                var photomaker = new PhotoMaker();
                var filter = new Filter();
    			
                // Adding ChangeBrightness filter
                Action<Photo> filterhandler = filter.ChangeBrightness;
    			
    	    //Adding ChangeContrast filter
    	    filterhandler += filter.ChangeContrast;
    			
    	    // Remove ChangeBrightness filter
                filterhandler -= filter.ChangeBrightness;
    			
    	    photomaker.Process("", filterhandler);	
    
                Console.Read();
            }
        }

  • Use a delegate in the following circumstances:
      • An eventing design pattern is used.
      • The caller has no need to access other properties, methods, or interfaces on the object implementing the method.

      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(); 
                }
            }