Sorting c#1 to c#4

Consider if we have list of Products and we want them to be ordered by Product Name, Below is how it might be implemented in c#1

In C# 1

class ProductNameComparer : IComparer
 {
   public int Compare(object x, object y)
   {
     ProductsSortingInCSharp1 first = (ProductsSortingInCSharp1)x;
     ProductsSortingInCSharp1 second = (ProductsSortingInCSharp1)y;

     return first.Name.CompareTo(second.Name);
   }
 }

     ArrayList products = ProductsSortingInCSharp1.GetSampleProducts();
     products.Sort(new ProductNameComparer());
     foreach(ProductsSortingInCSharp1 product in products)
     {
       Console.WriteLine(product.name);
     }

So as you can see to just sort the products we had to create a whole new class altogether, which has to implement IComparer, mention not this is only for one sort order, if we have to sort products both by name and price, then the job become tedious.

Also, we can see following issues with ProductNameComparer Class,

  • There are explicit casts in the code, casts are a way to tell the compiler that you know more information than it does and that means there is a chance that you might be wrong.ex: if ArrayList returning from GetSampleProducts were to return a string then there is a chance of run time error.

C#2 generics will address this issue,

List products = ProductsSortingInCSharp2.GetSampleProducts();
products.Sort(new ProductNameComparer());

class ProductNameComparer : IComparer<ProductsSortingInCSharp2>
 {
   public int Compare(ProductsSortingInCSharp2 first, ProductsSortingInCSharp2 second)
   {
     return first.Name.CompareTo(second.Name);
   }
 }

Still for comparing we have to create a class implementing an IComparable interface,  in C#2 this can be achieved using the delegate.

products.Sort(delegate (ProductsSortingInCSharp2 first, ProductsSortingInCSharp2 second)
{
  return first.Name.CompareTo(second.Name);
});

As you can see using delegates and anonymous methods in C#2 we had eliminated the creation of explicit class for comparison.

Only Problem I see with the above code is, readability is missing and C#3 lambda expression’s would come to our rescue here,

Sorting using Comparison from a lambda expression (C# 3)

List<ProductsSortingInCSharp2> products = ProductsSortingInCSharp2.GetSampleProducts();
 products.Sort((x, y) => x.Name.CompareTo(y.Name));
 foreach (ProductsSortingInCSharp2 product in products)
 {
   Console.WriteLine(product.Name);
 }

Ordering List using an extension method in C#3

    List<Product> products = Product.GetSamplesProducts();
     foreach (Product product in products.OrderBy(p =>p.Name))
     {
       Console.WriteLine(product);
     }

Please note that here we are NOT actually modifying the list in place, i.e if you print Products again you would see that the list has not been changed.

If we note the changes from C#1 to C#3 here it goes

  • C#1, Weakly typed comparator. No delegate sorting option
  • C#2, Strongly typed comparator. Delegate Comparisons and Anonymous methods
  • C#3, Lambda Expressions, Extension Methods and Option of leaving list Unsorted
Advertisements

The Product C Type(c#1)

Below is how typically how a product class might have written in c#1.

class ProductInCSharp1
    {
        string name;
        public string Name { get { return name; } }

        decimal price;
        public decimal Price { get { return price; } }

        public ProductInCSharp1(string name, decimal price)
        {
            this.name = name;
            this.price = price;
        }

        public static ArrayList GetSampleProducts()
        {
            ArrayList list = new ArrayList();
            list.Add(new ProductInCSharp1("fsdfadad", 9.0m));
            list.Add(new ProductInCSharp1("fsdfadad", 9.0m));
            list.Add(new ProductInCSharp1("fsdfadad", 9.0m));
            list.Add(new ProductInCSharp1("fsdfadad", 9.0m));
            return list;
        }

        public override string ToString()
        {
            return string.Format("{0} : {1}", name, price);
        }
    }

Looking through the code, there are three limitations that could be demonstrated.

  • An ArrayList has no compile time information about what is it contains, my mistake we can even add string instead of new ProductClass, since there is no compile time validation.
  • Here we only have public getters, if we were to have setters to Name and Price, they too would have to be public
  • There is soooo much code for simple public getter/setter properties

Strongly Typed Collections in c# 2

C#2 addresses first and second problems mentioned above, most important change is generics

class ProductInCSharp2
 {
   string name;
   public string Name
   {
     get { return name; }
     set { name = value; }
   }

   decimal price;
   public decimal Price
   {
     get { return price; }
     set { price = value; }
   }

   public ProductInCSharp2(string name, decimal price)
   {
     Name = name;
     Price = price;
   }

   public static List<ProductInCSharp2> GetSampleProducts()
   {
     List list = new List<ProductInCSharp2>();
     list.Add(new ProductInCSharp2("West Side Story", 9.99m));
     list.Add(new ProductInCSharp2("Assassins", 14.99m));
     list.Add(new ProductInCSharp2("Frogs", 13.99m));
     list.Add(new ProductInCSharp2("Sweeney Todd", 10.99m));
     return list;
   }

   public override  string ToString()
   {
     return string.Format("{0}: {1}", name, price);
   }
 }

Following are the changes to be noted

  • We have now private setters, which can be set through constructor
  • List would now only accept of type ProductInCSharp2. i.e, now we have compile time validation

Automatically Implemented Properties In C# 3

class ProductInCSharp3
 {
   public string Name { get; private set; }
   public decimal Price { get; private set; }
 
   public ProductInCSharp3(string name, decimal price)
   {
     Name = name;
     Price = price;
   }
 
   public ProductInCSharp3()
   {
       
   }
 
   public static List<ProductInCSharp3> GetSampleProducts()
   {
     return new List
     {
           new ProductInCSharp3 { Name="West Side Story", Price = 9.99m },
           new ProductInCSharp3 { Name="Assassins", Price=14.99m },
           new ProductInCSharp3 { Name="Frogs", Price=13.99m },
           new ProductInCSharp3 { Name="Sweeney Todd", Price=10.99m}
     };
   }
 
   public override string ToString()
   {
     return string.Format("{0}: {1}", Name, Price);
   }
 }

Following are the changes to be noted here

  • Now Properties dont have any code associated with them
  • we are building hardcoded list in a different way.With no name and price variables to access, we are forced to use the properties everywhere in the class, improving consistency.

Named Arguments in c# 4

class ProductInCSharp4
  {
    readonly string name;
    public string Name { get { return name; } }
 
    readonly decimal price;
    public decimal Price { get { return price; } }
 
    public ProductInCSharp4(string name, decimal price)
    {
      this.name = name;
      this.price = price;
    }
 
    public static List<ProductInCSharp4> GetSampleProducts()
    {
 
      return new List<ProductInCSharp4>
      {
          new ProductInCSharp4(name:"Naruto", price:33.33m),
          new ProductInCSharp4name: "Assassins", price: 14.99m),
          new ProductInCSharp4name: "Frogs", price: 13.99m),
          new ProductInCSharp4name: "Sweeney Todd", price: 10.99m)
      };
 
    }
 
  }

though the benefits of specifying named Arguments is minimal here, but if a method or constructor has too many parameters , it can make meaning of the code much clearer.(for example if you were having a nullable parameter type and you are passing null, with out named parameters it would be difficult to know for which parameter you are passing null.)

Summarizing evolution of c#1 to c#4

  • c#1 – Readonly properties, and weakly typed collections
  • c#2 – Private property setters and Strongly typed collections
  • c#3 – Automatically implemented properties, Enhanced object collection and initialization
  • c#4 – Named Arguments for clearer constructor and method calls

Delegates

We can think delegate type as a single method interface , and a delegate instance as an object implementing that interface.

As an example, consider your will, “pay the bills, make a donation to the charity,leave the rest of my estate to my cat” for instance you write it before your death and leave it in an appropiately safe place . After your death, your attorny will act on those instructions.”

A delegate in c# acts like your will in real world- it allows you to specify a sequence of actions to be executed at the appropriate time.

A Recipe for Simple Delegates

In order for a delegate to do anything , four things need to happen

  1. The delegate Type needs to be declared
  2. The code to be executed must be contained in a method
  3. The delegate instance must be created
  4. The delegate instane must be invoked

Example

class Program
 {
   delegate void StringProessor(string input);
   static void Main(string[] args)
   {
     Person jon = new Person("jon");
     Person tom = new Person("Tom");
 
     StringProessor jonsVoice, tomsVoie, background;
 
     jonsVoice = new StringProessor(jon.Say);
     tomsVoie= new StringProessor(tom.Say);
     background = new StringProessor(Background.Note);
 
     jonsVoice("HelloSon");
     tomsVoie.Invoke("Hello, Dady!");
     background("An Airplae files past");
   }
 }
 
 class Person
 {
   string name;
 
   public Person(string name)
   {
     this.name = name;
   }
 
   public void Say(string message)
   {
     Console.WriteLine("{0} says: {1}", name , message);
   }
 }
 
 class Background
 {
   public static void Note(string note)
   {
     Console.WriteLine("{0}",note);
   }
 }