tags:

views:

186

answers:

6

How do I add two numbers together when I don't know their type in .NET? For example, how would you implement the following function?

public object AddTwoNumbers(object left, object right)
{
    /* What goes here? */
}

Assume that the 'left' and 'right' parameters are (boxed) value type such as Int32, double, Decimal, etc. You don't know the specific type, you just know that it's numeric and that addition makes sense for it.

Thanks!

+1  A: 

Safest way (may no be prettiest) is to use reflection and assign them to appropriated typed variables and execute the add operation.

Otávio Décio
Can this be done cleanly or does it boil down to a gigantic switch statement containing every value type?
+11  A: 

In .NET 3.5 and before, the simplest way is probably to test for every type - or rather, every combination of types - that you can handle, cast appropriately and perform the relevant operation. You could try to fetch the appropriate operator with reflection, but that's likely to have some odd corner cases.

In .NET 4.0 and C# 4, this is easy though:

public object AddTwoNumbers(object left, object right)
{
    dynamic x = left;
    dynamic y = right;
    return x + y;
}

If you're willing to have the restriction that left and right must be the same type, it's not too bad:

using System;
using System.Collections.Generic;

public class Test
{
    static Dictionary<Type, Func<object, object, object>> Adders = 
        new Dictionary<Type, Func<object, object, object>>
    {
        { typeof(int), (x, y) => (int) x + (int) y },
        { typeof(double), (x, y) => (double) x + (double) y },
        { typeof(decimal), (x, y) => (decimal) x + (decimal) y },
        { typeof(long), (x, y) => (long) x + (long) y },
        // etc
    };

    static object Add(object left, object right)
    {
        if (left.GetType() != right.GetType())
        {
            throw new ArgumentException("Types must be the same");
        }
        Func<object, object, object> adder;
        if (!Adders.TryGetValue(left.GetType(), out adder))
        {
            throw new ArgumentException
                ("I don't have an adder for that type");
        }
        return adder(left, right);
    }

    static void Main()
    {
        Console.WriteLine(Add(3, 4));
        Console.WriteLine(Add(3.5m, 4.2m));
        Console.WriteLine(Add(3.5, 4.8));
    }
}
Jon Skeet
Thanks Skeet. I appreciate your .net 4.0 knowledge sharing.
adatapost
Yes they are of the same type... like the compactness of this solution vs. case/if structure.. thanks.
Smooth move, Jon.
Alex Baranosky
case/if is compact too, provided that your code style policy isn't too restrictive.
Anton Tykhyy
+2  A: 

I don't know the specifics of what you're trying to achieve but I'd strongly recommend not boxing value types it's really not an efficient approach as the CLR will construct a new object in place on the heap.
You could do something horrible like checking the type of left and right but then what happens if left isn't the same type as right? In that case which type takes precedence? i.e if left is Int32 and right is float?

zebrabox
+2  A: 

You can't add values if you don't know how they are represented. A float and an int use the same amount of memory (32 bits) but have completely different binary representations (i.e. the same binary pattern has a completely different value if you interpret it as an int or a float).

What you have to do is convert the values to a few common base types that you can add - for example, doubles and longs. This would work fine for any values of lower or equal precision, but still would not allow you to safely/accurately represent larger values (e.g. you still wouldn't be able to correctly handle decimals or int128s)

The dynamic type in .net 4.0 allows you to do this easily, but "under the bonnet" it's still doing the same thing - converting the types to ones it can handle.

Jason Williams
+4  A: 

You can't add anything that you don't know the type of, even if you are certain that it's possible to add them. You have to convert them to a specific type. To be safe from overflows you can convert both to Double as it can handle the range of the other types:

public object AddTwoNumbers(object left, object right) {
   return Convert.ToDouble(left) + Convert.ToDouble(right);
}

However, that doesn't guarantee the precision of the numbers.

You can create overloaded methods to handle different types:

public int AddTwoNumbers(int left, int right) {
   return left + right;
}

public double AddTwoNumbers(double left, double right) {
   return left + right;
}

That of course requires that the type is known at compile time, and doesn't add anything over the + operator.

If neither of those do what you want, I guess that you have to write a lot of if statements checking the type of the arguments to do the correct casting:

public object AddTwoNumbers(object left, object right) {
   if (left is int && right is int) {
      return (int)left + (int)right;
   } else if (left is double && right is duble) {
      return (double)left + (double)right;
   } else ...
      ...
   } else {
      throw new ArgumentException("Arguments could not be added.");
   }
}
Guffa
Thanks for the length of this answer. I'm going with Skeet's as it has the nifty syntax but I think the people I work with might argue your version is simpler hence easier to maintain.
A: 

You could also use the generic type 'dispatch' method employed by F#.

The code to implement it is quite lengthy, but at least you get

  • type safety
  • no unnecessary boxing
  • implicit conversion support

The approach is similar to what Jon does above.

leppie