views:

155

answers:

2

Given two shorts (System.Int16)

short left = short.MaxValue;
short right = 1;

I want to get an OverflowException when adding them.

checked(left+right)

does not work, because the result of left+right is an Int32.

checked((short)(left+right))

works as expected.

My problem is that, using Expression Trees, the "trick" doesn't work:

var a = Expression.Constant(left);
var b = Expression.Constant(right);
var sum = Expression.ConvertChecked(Expression.Add(a, b), typeof(short));
var l = Expression.Lambda(sum);
var f = (Func<short>)l.Compile();

Calling f() does not throw an overflow exception but returns -32768. What's wrong?

+3  A: 

The problem is that the addition is being done as short + short (which presumably exists in IL even if it doesn't exist in C#) - and then the conversion is performed separately. This is shown by this complete program - even without the conversion, the result is -32768:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main(string[] args)
    {
        short left = short.MaxValue;
        short right = 1;
        var a = Expression.Constant(left);
        var b = Expression.Constant(right);
        var sum = Expression.Add(a, b);
        var convert = Expression.ConvertChecked(sum, typeof(short));
        var convertLambda = Expression.Lambda<Func<short>>(convert);
        var convertFunc = convertLambda.Compile();
        Console.WriteLine("Conversion: {0}", convertFunc());
        var sumLambda = Expression.Lambda<Func<short>>(sum);
        var sumFunc = sumLambda.Compile();
        Console.WriteLine("Sum: {0}", sumFunc());
    }
}

If you make it do an int + int addition and then the conversion, it will throw an overflow exception:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main(string[] args)
    {
        short left = short.MaxValue;
        short right = 1;
        var a = Expression.Constant((int) left);
        var b = Expression.Constant((int) right);
        var sum = Expression.Add(a, b);
        var convert = Expression.ConvertChecked(sum, typeof(short));
        var convertLambda = Expression.Lambda<Func<short>>(convert);
        var convertFunc = convertLambda.Compile();
        Console.WriteLine("Conversion: {0}", convertFunc());
        var sumLambda = Expression.Lambda<Func<int>>(sum);
        var sumFunc = sumLambda.Compile();
        Console.WriteLine("Sum: {0}", sumFunc());
    }
}

I don't know why AddChecked doesn't work though... that looks like a bug :( It's possible that using the overload which allows the Method to specified would work, but I'm not sure...

Jon Skeet
Excellent explanation.
David
A: 

Thanks.

With other words: What you see in C# cannot be translated 1:1 to Expression Trees. I wonder why they decided to implicitly widen the sum of two shorts to int.

Rauhotz
If I understand Jon's explanation correctly. It is not widening the sum of two shorts to an int. It's simply adding the two shorts just as if you had done: short sum = (short)(left + right); // Gives -32768Then it does the conversion of sum (-32768) into a short, -32768 is a valid short value.
David
Yes. There are two issues here - there's the fact that C# doesn't *have* short+short - it automatically widens to int+int, whereas IL apparently doesn't. Then there's the way that AddChecked appears to be a bit broken :(
Jon Skeet
The 1:1 conversion is the second part of my answer - do it as int+int and then do the checked conversion. I think, anyway :)
Jon Skeet
Goodbye my elegant implementation of a generic numeric calculator ;-)
Rauhotz