If:
- you just need a simple solution in the program
- your little language has the same arithmetic rules as C #
- you can use c # 4
- you don't really like performance
then you can just do this:
public static object Add(dynamic left, dynamic right) { return left + right; }
Done. What happens when this method is called, the code starts the C # compiler again and asks the compiler: "What would you do if you had to add these two things, but did you know their types of execution at compile time?" (The Dynamic Language Runtime then caches the result so that the next time someone tries to add two ints, the compiler does not start again, they just reuse the lambda that the compiler returned to DLR.)
If you want to implement your own rules for adding, welcome to my world. There is no magic road that avoids a lot of checks and type switches. There are literally hundreds of possible cases for adding two arbitrary types, and you should check them out.
How we deal with this complexity in C #, we define addition operations on a smaller subset of types: int, uint, long, ulong, decimal, double, float, all enums, all delegates, string, and all valid versions of these value types. (Then the enumerations are considered as their basic types, which simplifies further actions.)
So, for example, when you add an abbreviation to an abbreviation, we simplify the problem by saying that ushort and short are both special cases of int, and then solve the problem of adding two ints. This greatly reduces the amount of code we need to write. But believe me, the algorithm for converting binary operators to C # is many thousands of lines of code. This is not an easy task.
If your toy language is designed for a dynamic language with its own rules, you might consider introducing IDynamicMetaObjectProvider and using DLR mechanisms to implement arithmetic and other operations, such as calling a function.