Thursday, 16 February 2017

IComparer

If you inspect the overloaded Sort() method for collections you'll see that it contains
public void Sort(IComparer<T> comparer);
and this version is a perfect example of the inversion of control pattern, the method expects a class that implements the IComparer interface, but leaves it up to the user as to how the implementation will work. here's the definition for the interface.

namespace System.Collections.Generic
{
// Summary:
//     Defines a method that a type implements to compare two objects.
//
// Type parameters:
//   T: The type of objects to compare. This type parameter is contravariant.
    public interface IComparer<in T>
    {
// Summary:
//     Compares two objects and returns a value indicating whether one is less than,
//     equal to, or greater than the other.
//
// Parameters:
//   x: The first object to compare.
//   y: The second object to compare.
//
// Returns:
//     A signed integer that indicates the relative values of x and y, as shown in the
//     following table.
//     Value                Meaning
//     Less than zero       x is less than y.
//     Zero                 x equals y.
//     Greater than zero    x is greater than y.
        int Compare(T x, T y);
    }

}

now we'll create a comparer for the following Person class

class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }

    public int Age { get {
            var today = DateTime.Today;
            var age = today.Year - BirthDate.Year;

            return BirthDate > today.AddYears(-age) ? --age : age;
        }
    }

    public override string ToString()
    {
        return $"{Age} {FirstName} {LastName}";
    }

}

pretty standard class, exactly what we used in the previous post about IComparable<T>. Now let's go ahead and create our PersonComparer class which implements the IComparer<T> Interface.

class PersonComparer : IComparer<Person>
{
    PropertyInfo PropertyToSortOn = typeof(Person).GetRuntimeProperty("LastName");

    public PersonComparer(PropertyInfo Property)
    {
        if (typeof(Person).GetRuntimeProperties().Contains(Property))
            PropertyToSortOn = Property;
    }

    public int Compare(Person x, Person y)
    {
        switch (PropertyToSortOn.Name) {
            case "FirstName":
                return x.FirstName.CompareTo(y.FirstName);
            case "LastName":
                return x.LastName.CompareTo(y.LastName);
            default:
                return x.Age.CompareTo(y.Age);
        }  
    }

}

we used a little reflection here, to specify which property we want to sort on, and in this way we can define how we want to sort separately from our Person class.

class PersonComparer : IComparer<Person>
{
    public int Compare(Person x, Person y)
    {
        var lastName = x.LastName.CompareTo(y.LastName);
        if (lastName == 0)
            return x.FirstName.CompareTo(y.FirstName);
        return lastName;
    }

}

for good measure i also created a class that sorts by last name and if the last names are equal then sorts by first name.

anyway here's a main we can use to test our co

using System;
using System.Collections.Generic;

namespace pc.IComparer
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public int Age
        {
            get
            {
                var today = DateTime.Today;
                var age = today.Year - BirthDate.Year;

                return BirthDate > today.AddYears(-age) ? --age : age;
            }
        }
        public override string ToString()
        {
            return $"{Age} {FirstName} {LastName}";
        }
    }

    class PersonComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            var lastName = x.LastName.CompareTo(y.LastName);
            if (lastName == 0)
                return x.FirstName.CompareTo(y.FirstName);
            return lastName;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person
            {
                FirstName = "Tomek",
                LastName = "Chooch",
                BirthDate = new DateTime(1988, 8, 28)
            };
            var p2 = new Person
            {
                FirstName = "Pawel",
                LastName = "Chooch",
                BirthDate = new DateTime(1984, 6, 28)
            };
            var p3 = new Person
            {
                FirstName = "Marin",
                LastName = "Smartz",
                BirthDate = new DateTime(1983, 11, 3)
            };
            var p4 = new Person
            {
                FirstName = "Magda",
                LastName = "Chooch",
                BirthDate = new DateTime(1984, 1, 31)
            };
            var p5 = new Person
            {
                FirstName = "Jakeb",
                LastName = "Tywonk",
                BirthDate = new DateTime(1988, 5, 4)
            };
            var p6 = new Person
            {
                FirstName = "Ivan",
                LastName = "Pendaz",
                BirthDate = new DateTime(1986, 3, 3)
            };
            var p7 = new Person
            {
                FirstName = "Ivan",
                LastName = "gajic",
                BirthDate = new DateTime(1983, 3, 12)
            };
            var p8 = new Person
            {
                FirstName = "Ivana",
                LastName = "Trudu",
                BirthDate = new DateTime(1986, 3, 1)
            };

            var ppl = new List<Person>(new Person[] { p1, p2, p3, p4, p5, p6, p7, p8 });

            var pc = new PersonComparer();
            ppl.Sort(pc);

            foreach (var p in ppl)
                Console.WriteLine(p.ToString());
        }
    }
}


Wednesday, 15 February 2017

IComparable

IComparable<T> defines a generic function that facilitates the sorting of instances of the type that implements it. For example if we had our person class and implemented the "CompareTo" function;

class Person : IComparable<Person>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public int Age
    {
        get
        {
            var today = DateTime.Today;
            var age = today.Year - BirthDate.Year;
            return BirthDate > today.AddYears(-age) ? --age : age;
        }
    }

    public override string ToString()
    {
        return $"{Age}) {FirstName} {LastName}";
    }

    public int CompareTo(Person other)
    {
        return Age - other.Age;
    }

}

we see that the "CompareTo" function has three types of results,

  • a negative int: which means that the other person is has a higher age and will be to the right of the current person
  • a zero: which mean that the two people are of equal age and will be next to each other
  • a positive int: meaning that the other person is younger and will be to the left of the current Person.

Now let's demonstrate the IComparable interface in action

using System;
using System.Collections.Generic;

namespace pc.IComparable
{
    class Person : IComparable<Person>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public int Age
        {
            get
            {
                var today = DateTime.Today;
                var age = today.Year - BirthDate.Year;
                return BirthDate > today.AddYears(-age) ? --age : age;
            }
        }

        public override string ToString()
        {
            return $"{Age}) {FirstName} {LastName}";
        }

        public int CompareTo(Person other)
        {
            return Age - other.Age;
        }
    }

class Program
{
    static void Main(string[] args)
    {
        var p1 = new Person { FirstName = "Tomek", LastName = "Chooch",
            BirthDate = new DateTime(1988, 8, 28) };
        var p2 = new Person { FirstName = "Pawel", LastName = "Chooch",
            BirthDate = new DateTime(1984, 6, 28) };
        var p3 = new Person { FirstName = "Marin", LastName = "Smartz",
            BirthDate = new DateTime(1983, 11, 3) };
        var p4 = new Person { FirstName = "Magda", LastName = "Chooch",
            BirthDate = new DateTime(1984, 1, 31) };
        var p5 = new Person { FirstName = "Jakeb", LastName = "Tywonk",
            BirthDate = new DateTime(1988, 5, 4) };
        var p6 = new Person { FirstName = "Ivanp", LastName = "endasd",
            BirthDate = new DateTime(1986, 3, 3) };
        var p7 = new Person { FirstName = "Jakes", LastName = "gadicd",
            BirthDate = new DateTime(1986, 3, 2) };
        var p8 = new Person { FirstName = "Bober", LastName = "asdasd",
            BirthDate = new DateTime(1986, 3, 1) };

        var ppl = new List<Person>(new Person[] { p1, p2, p3, p4, p5, p6, p7, p8 });
        ppl.Sort();

        foreach (var p in ppl)
            Console.WriteLine(p.ToString());
    }
}

As you can see we create 8 instances of our person class put them into an array, sort it and then output them in their sorted by age.


Tuesday, 14 February 2017

IEquatable

The IEquatable defines a generalized function that a class implements to determine if two instances are "equal". If we create a basic implementation of a "Person" class

class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }

}

And in our main we create two instances of the person class, both of which have the same first and last name

using System;

namespace pc.IEquatableExample
{
    class Person {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person { FirstName = "Pawel", LastName = "Ciucias" };
            var p2 = new Person { FirstName = "Pawel", LastName = "Ciucias" };

            Console.WriteLine($"p1 is the same as p2:" + p1.Equals(p2));
        }
    }
}

but if we run the code above, p1.Equals(p2) returns false because by default the equals function checks if the the two objects reference the same object. if we had something like the following

static void Main(string[] args)
{
    var p1 = new Person { FirstName = "Pawel", LastName = "Ciucias" };
    var p2 = p1;

    Console.WriteLine($"p1 is the same as p2:" + p1.Equals(p1));

}

our result would be true, however if we implement the IEquatable<Person> we could compare two different instances of our person class and determine if by their properties the are equal instead of their reference.

using System;

namespace pc.IEquatableExample
{
    class Person : IEquatable<Person>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool Equals(Person other)
        {
            return FirstName == other.FirstName &&
                LastName == other.LastName;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Person { FirstName = "Pawel", LastName = "Ciucias" };
            var p2 = new Person { FirstName = "Pawel", LastName = "Ciucias" };

            Console.WriteLine($"p1 is the same as p2:" + p1.Equals(p1));
        }
    }

}

in the above because we implemented IEquatable in our Person class p1.Equals(p2) returns true.