C# 3.0 – Variance Explained

The problem:

Why can’t I create a List of type Dog and assign it to a List of type Animal?

IList<Animal> animals = new List<Dog>(); // no good

Theory:

There are 3 terms relating to variance:

Covariance – allows more specific types to be assigned to more general types. (i.e. sub-types (classes, interfaces) can be assigned to any types (classes, interfaces) that they inherit from).

C# Example: Method Return types are Covariant. We can return a sub-type of the method’s declaring return type.


IAnimal GetAnimal(string animalName) {...};

GetAnimal("dog") {return new Dog();} // the dog is more specific and returned as the general type IAnimal
GetAnimal("cat") {return new Cat();} // the cat is more specific and returned as the general type IAnimal

Contravariance – allows general types to accept more specific types – i.e. The reverse of covariance.

C# Example: Method parameters are Contravariant. We can call a method with a parameter that is a sub-type of the parameters declaring type.


IAnimal GetAnimal(IAnimal animal) {...};
GetAnimal(new Dog()); // the method takes a general type IAnimal but is called with the more specific type Dog
GetAnimal(new Cat()); // the method takes a general type IAnimal but is called with the more specific type Dog

Invariance – occurs when neither of these conditions are met.

C# Example: In C# 3.0 Generics are invariant. C# 4.0 allows the variance of generics to be defined (with restrictions).


IList<Animal> animals = new IList<Animal>();
animals.Add(new Dog());
animals.Add(new Cat()); 

IList<Animal> animals = new List<Dog>();
animals.Add(new Dog());
animals.Add(new Cat()); // no dice. You can’t assign a cat to a list of dogs.

Posted in .Net, C# at July 30th, 2009. 2 Comments.

Two nice string extensions

Here’s a couple (well, one is an overload of the other) of string extension methods I wrote today which I’m quite fond of:


/// <summary>
/// Determines if any of the given matches are a case sensitive match for the
/// nominated string value
/// </summary>
/// <param name="text">The string value to be checked</param>
/// <param name="matches">The list of potential matches</param>
static public bool IsIn(this string text, params string[] matches)
{
    return IsIn(text, true, matches);
}

/// <summary>
/// Determines if any of the given matches are a case sensitive match for the
/// nominated string value
/// </summary>
/// <param name="text">The string value to be checked</param>
/// <param name="caseSensitive"><c>true</c> to perform a case
/// sensitive match</param>
/// <param name="matches">The list of potential matches</param>
static public bool IsIn(this string text, bool caseSensitive, params string[] matches)
{
    StringComparison comparer = caseSensitive
        ? StringComparison.CurrentCulture
        : StringComparison.CurrentCultureIgnoreCase;

    return matches.Any(value => text.Equals(value, comparer));
}


So instead of writing this:


if (name == "James" || name == "Peter" || name == "Sally")
    Console.WriteLine("Your name is too generic!");

You can simply write this:


if (name.IsIn("James", "Peter", "Sally")
    Console.WriteLine("Your name is too generic!");

No offence to the James, Peters, and Sallys of the world.

Posted in .Net, C# at July 9th, 2009. No Comments.