C# in Depth

Cover of C# in Depth
Order now (3rd edition)

Summing a sequence dynamically

In an early draft of the book, most of the section demonstrating the use of dynamic typing in purely managed code was dedicated to the task of summing a sequence dynamically. A change to the behaviour of the language made me revisit that section and amend it to include a wider variety of examples, but the task of dynamic summation is still interesting enough to be worth documenting. This article will go into some of the possible options. To make it more approachable for visitors who don't have the book, I'll start from scratch rather than just continuing from where the book left off. (This also means if there are any areas in the book which didn't make sense to you, there's a chance that this page will provide an alternative explanation which may "click" better.)

What are we trying to achieve?

Enumerable.Sum in LINQ to Objects has many, many overloads - and even so, it won't work for everything. For example, we might want to sum TimeSpan values - or short integers, or unsigned integers - but none of those scenarios is catered for in the framework. Let's see if we can do better. It would be nice if we could express a statically typed generic solution with a constraint that the type must have a public static T operator +(T, T) operator, but that option isn't available to us. Instead, we'll use dynamic typing.

Choosing a signature: trying IEnumerable<dynamic>

The first choice we need to make is what signature we want for our DynamicSum method. One option would be to use IEnumerable<dynamic> as a parameter, and return dynamic as well. You would think this would be easy, but it has two significant issues:

For the sake of showing what the code might look like, let's take the zero value as another parameter. Another alternative might be to throw an exception if the sequence is empty, as methods like Max do. Here's an implementation:

public static class DynamicExtensions
{
    public static dynamic DynamicSum(this IEnumerable<dynamic> source, dynamic zero)
    {
        dynamic total = zero;
        foreach (var item in source)
        {
            total = total + item;
        }
        return total;
    }
}

That looks about as simple as it gets - although many of the corner cases we'll run into later also apply here. Let's try it out:

public class Test
{
    public static void Main()
    {
        TimeSpan oneHour = TimeSpan.FromHours(1);
        TimeSpan tenMinutes = TimeSpan.FromMinutes(10);
        TimeSpan threeSeconds = TimeSpan.FromSeconds(3);
        
        dynamic[] values = new dynamic[] { oneHour, tenMinutes, threeSeconds};
        dynamic timeSpanSum = values.DynamicSum(TimeSpan.Zero);
        Console.WriteLine("Sum with 'zero' as time span: {0}", timeSpanSum);
        
        DateTime now = DateTime.Now;
        Console.WriteLine("Current date/time: {0}", now);
        dynamic dateTimeSum = values.DynamicSum(now);
        Console.WriteLine("Sum with 'zero' as date/time: {0}", dateTimeSum);
    }
}

Here we're using a fairly natural "zero" value the first time - TimeSpan.Zero. In the second call, we're providing the current date and time. It may not sound like a zero to you or me, but it's good enough for the method - it's happy to add TimeSpan values to DateTime values, with the return value being another DateTime at each step. Here are the results of one run:

Sum with 'zero' as time span: 01:10:03
Current date/time: 04/11/2010 07:39:24
Sum with 'zero' as date/time: 04/11/2010 08:49:27

That's as far as I want to go with IEnumerable<dynamic>. Let's take a different approach now.

Summing a statically typed sequence dynamically

Okay, so we'll restrict ourselve to the more common requirement of summing values all of one type (or at least of a compatible type). Now we'll be okay to sum a byte array, or a List<TimeSpan> or whatever.

We still have the knotty question of what to use as a zero point, but at least now we know the source and result type, we can take the default value for that type. This does have some downsides, as we'll see later, but let's keep it simple to start with.

public static T DynamicSum<T>(this IEnumerable<T> source)
{
    T total = default(T);
    foreach (dynamic item in source)
    {
        total = total + item;
    }
    return total;
}

This is almost the same code as we had before, but using T in most of the places we had dynamic before. In fact, dynamic only comes up in a single place: the declaration of item.

Unfortunately, this code isn't quite right. It breaks if you pass it a byte array, with this error (at execution time):

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
   Cannot implicitly convert type 'int' to 'byte'.
   An explicit conversion exists (are you missing a cast?)
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at DynamicExtensions.DynamicSum[T](IEnumerable`1 source)
   at Test.Main()

The problem is this line:

total = total + item;

The type of the expression on the right-hand side of the assignment operator is dynamic, because it's the result of a dynamic addition. We're then trying to assign it back to total. Now when we try to add two bytes together, the result is an int - and there's no implicit conversion from byte to int. There's an explicit conversion though, so we can get round the problem by just including that:

public static T DynamicSum<T>(this IEnumerable<T> source)
{
    T total = default(T);
    foreach (dynamic item in source)
    {
        total = (T) (total + item);
    }
    return total;
}

Note that you'd have to do exactly the same thing in a statically typed method summing an array of bytes... unless you used something like this:

total += item;

The C# spec explicitly translates a compound assignment expression like this to have an implicit cast. Unfortunately, although this worked in early betas of .NET 4 in a dynamic context, it doesn't work now. Arguably the more explicit version shows what we're doing more clearly anyway. In particular, it naturally suggests that the cast could fail for some reason. In our dynamic case, this could happen if we had an addition operator which returned a different type, or (as in our byte and int case) if the result ended up out of the range of the original type. Whether you would want overflow to cause an exception or not is a design decision - you might even want to have two methods, one with a checked block and one with an unchecked block, to let the caller decide.

Handling nullable types

The overloads for Enumerable.Sum which take sequences of nullable types (such as IEnumerable<int?>) don't behave quite the same way as the others. If we were to add every item in the sequence, the result would be null - because adding anything to a null value results in a null value, according to the rules of C# lifted operators on nullable types.

Instead, the normal LINQ to Objects methods just skip any null values. Even though the return types of the methods are nullable, the returned value is never null, even if the input sequence is empty or only contains null values.

We could try to approach this by making the addition conditional on the item being null in a simple way:

// Warning: unhelpful code. See note below.
public static T DynamicSum<T>(this IEnumerable<T> source)
{
    T total = default(T);
    foreach (dynamic item in source)
    {
        if (item != null)
        {
            total = (T) (total + item);
        }
    }
    return total;
}

Unfortunately, this doesn't work properly when using nullable types because the total variable ends up with the value of null to start with - so even if the sequence doesn't contain any null values, we'll end up with a null result.

We could fix this to some extent by finding the first non-null value and then summing any non-null values thereafter - but we'd still have the problem of what to do with an empty sequence, or one only containing null values. To follow the LINQ to Objects model, this should still return the relevant zero value, instead of null.

The simplest approach is just to add an overload for the nullable value case, leaving the original code in place. If we make the type parameter the non-nullable type, but accept a sequence of the nullable type, we can get the appropriate zero value as normal:

public static T? DynamicSum<T>(this IEnumerable<T?> source)
    where T : struct
{
    T total = default(T);
    foreach (T? item in source)
    {
        if (item != null)
        {
            dynamic value = item.Value;
            total = (T) (total + value);
        }
    }
    return total;
}

That does leave one problem: if we call DynamicSum from a generic method which doesn't know whether or not it's dealing with a nullable type. That sounds like a job for dynamic typing, and indeed we can call the right method appropriately at execution time, like this:

public static dynamic VeryDynamicSum(this object source)        
{
    dynamic dynamicSource = source;
    return DynamicSum(dynamicSource);
}

Note that we can't make the source parameter dynamic, as you can't write extension methods targeting dynamic... but targeting object is almost as good. I'd argue that the name could be better chosen, admittedly...

Conclusion

There are, of course, other approaches that we could take when dynamically summing a sequence. Hopefully this article has at least provided you with a few thoughts about the kind of issues you should think about when dealing with dynamic typing.