Category Archives: c#

Extensibility through Delegates

One of the key tenets of the SOLID principles used in object oriented design is the Open/Closed Principle. This states that software entities should be open for extension but closed for modification. This rather cryptic statement means that objects shouldn’t have to be changed every time requirements change. Clearly this is not always possible but objects can be made more tolerant to change through OO techniques such as inheritance and polymorphism. This is typically implemented using interfaces or abstract base classes that push responsibility out to concrete classes. Here is an excellent example.

However, this isn’t the only means to achieve this. C# is a rich language which provides multiple, complementary mechanisms to solve similar problems. Through delegates and lambdas, an alternative approach can be used. The following example demonstrates how a class can be extended without modification by allowing the caller to supply their own implementation.

internal class Helper
{
    internal static string UpdateString(string value)
    {
        return value.ToUpper();
    }
        
    internal static string UpdateString(Func<string> f)
    {
        return f();
    }
          
}

String s = "Hello";
Console.WriteLine(Helper.UpdateString(s));
Console.WriteLine(Helper.UpdateString(() => s.ToLower()));

Output

HELLO
hello

This simple example demonstrates how the Helper class can be offer an extensibility point via a method overload that accepts a delegate (in this case a Func<string>). The caller is able to override the default implementation which converts the string to upper case by providing its own implementation, in the form of a lambda expression.

For more on lambdas and delegates see my previous post on Linq.

Advertisements

The C# Null Coalesce Operator

This post was written partly as a reminder to myself that the C# null coalesce operator exists and can be used effectively to write concise readable code. But what is it?

The Null Coalesce operator in C# is a shorthand way of returning a value but specifying a default if that value is null. This is easily demonstrated using a quick example.

String text = "Hello";
String greeting = text ?? "The greeting was null";

In this case, greeting is assigned the value of text unless text is null. In that case it is given a default value of “The greeting was null”. Clearly this could be achieved by other means such as an if..else statement or a ternary operator, however this method provides a very terse syntax. In addition it deals well with results of direct method calls. For example:

String value = MyFunction() ?? "No value returned";

is much better than

String value2 = MyFunction() != null ? MyFunction() : "No value returned";

and more efficient too as the function is only called once.

It should be noted though that it only deals with nulls and understandably does not handle empty strings. For this the String.IsNullOrEmpty can be used instead.

The Missing Linq

Recently I seem to have been finding more and more old code that would benefit from refactoring using Linq. Whilst still valid, much of the code I see can be significantly improved in terms of clarity and conciseness. Let’s look at a simple example.

The scenario I’ll be using here involves filtering a collection of Person objects to find those with an age lower than 30. First let’s add some boilerplate code as a basis to work with.

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

List<Person> people = new List<Person>()
{
    new Person() { Id = 1, Name = "First Person", Age = 67},
    new Person() { Id = 2, Name = "Second Person", Age = 18},
    new Person() { Id = 3, Name = "Third Person", Age = 42},
    new Person() { Id = 4, Name = "Fourth Person", Age = 8}
};

private static void PrintListOfPeople(string title, IEnumerable<Person> people)
{
    Console.WriteLine(title);
    foreach (var person in people)
    {
        Console.WriteLine("[{0}] {1}, aged {2}", person.Id, person.Name, person.Age);
    }
    Console.WriteLine();
}


Here we have a Person class, a collection of 4 Person instances and a function to display the collection in the console. All pretty straightforward and nothing too exciting. The aim is to take the collection and filter it so that only the people aged under 30 remain.

To do so without Linq one might use the following approach.

// Without Linq
IList<Person> youngPeople = new List<Person>();
foreach (var person in people)
{
    if (person.Age < 30)
        youngPeople.Add(person);
}
PrintListOfPeople("List without Linq", youngPeople);

This code is pretty simple and it’s probably similar to code you’ve written hundreds of times in the past – initialise a new collection, iterate through the source collection, test each value and if there is a match add it to the new collection. No problems, works very well.

Now consider how the same code could be written using Linq. Note this code requires an Import of the System.Linq namespace.

// With Linq
IEnumerable<Person> youngPeopleLinq = people.Where(p => p.Age < 30);
PrintListOfPeople("List with Linq", youngPeopleLinq);

Wow, we’ve managed to condense 7 lines into 2 and arguably produced code that is more readable. When I first saw the LInq syntax it seemed a bit alien though I think that was more down to the lambda expression than anything else. Let’s dissect this code and see how it works.

Linq Fundamentals

At the high level, Linq really is just a series of extension methods to IEnumerable that offer additional ways to work with collections. In the above example I used the Where extension method. There are many other methods available. So far, so good but what is going on with that crazy lambda expression? To explain, consider the method signature of Where.

The Where extension method takes as a parameter a Func<Person,bool>. For those unfamiliar with Func, this is a language feature which provides a predefined delegate with a particular signature. It just dispenses with the need to define your own delegate. In this case our Func takes a parameter of type Person and returns a bool i.e.

   bool MyFunction(Person p)

So we have the Where extension method which needs to be supplied with a function that accepts a Person object and returns a boolean. The Where method will pass each item in the collection to the function and the boolean returned will indicate if the item being examined should be included or excluded in the resultset. The function itself must apply the test criteria.

We could therefore just create a function which does this test and pass that to the Where method. For example:

IEnumerable<Person> youngPeopleLinq = people.Where(CheckPersonAge);

private static bool CheckPersonAge(Person p)
{
    return (p.Age < 30);
}

Whilst this is a perfectly valid approach, the use of a Lambda expression provides a more concise syntax. Let’s look more closely at the lambda used in the original example.

Lambda Expressions 101

A Lambda Expression is a shorthand syntax for an anonymous function. It uses the => operator which translates as “goes into”. So the expression p => p.Age < 30 is translated to “pass p into the expression p.Age < 30”. So p is simply the name of the Person parameter passed into the expression which returns the boolean result of the test p.Age < 30. To summarise, p => p.Age < 30 is equivalent to the CheckPersonAge function shown earlier but with a much more concise syntax.

The lambda syntax confused the heck out of me when I first encountered it. My initial thoughts were “what is ‘p’, it’s not defined anywhere?”  The key thing to remember here is that the compiler is expecting a delegate with a parameter of type Person. All we need to do is provide a name for that parameter so that is can be used inside the target expression.

Linq-ing the Pieces

Hopefully this whirlwind tour of Linq and Lambdas has provided some food for thought. Linq does not really provide anything you could not achieve by other means. However, it does offer a powerful, concise syntax for working with collections. I would encourage any .NET developer to explore the capabilities of Linq and include it as another option in the developer toolbox.