Doing arithmetic on generic parameters
The following code produces compiler error CS0019
public T Add<T> (T a, T b)
{
return a + b;
}
public T Add<T> (T a, T b) where T:ValueType
{
return a + b;
}
The following code produces compiler error CS0019
public T Add<T> (T a, T b)
{
return a + b;
}
public T Add<T> (T a, T b) where T:ValueType
{
return a + b;
}
Only for you. Only today ;-)
using System;
using System.Reflection;
namespace TestApp
{
class My
{
public static My operator +(My x, My y)
{
return x;
}
}
public static class PrimiteImpl
{
// Define operations here for all primitive classes
public static int Add(int x, int y)
{
return x + y;
}
}
public static class MathOps<T>
{
delegate T BinaryOp(T a, T b);
static readonly BinaryOp add;
static MathOps()
{
// For primitive types we need to emulate this functionality manualy
if (typeof(T).IsPrimitive)
{
add = (BinaryOp)Delegate.CreateDelegate(typeof(BinaryOp), typeof(PrimiteImpl), "Add", false, false);
}
else
{
// For user-defined custom types - use type from user
add = (BinaryOp)Delegate.CreateDelegate(typeof(BinaryOp), typeof(T), "op_Addition", false, false);
}
// If not found do not leave our delegate as empty to avoid useless null-checks
// but assign it to method that will throw some valuable information to user
// It's up to you to use this hack or throw some custom exception as early as possible
if (add == null)
{
add = new BinaryOp(NotSupported);
}
}
// Requered to avoid TypeInitializationException then no operation can be found
private static T NotSupported(T dummy1, T dummy2)
{
throw new NotSupportedException(string.Format("I'm realy sorry. {T} does not support some requered operations.",typeof(T).FullName));
}
public static T Add(T x, T y)
{
return add(x, y);
}
}
class Program
{
public static T Add<T>(T a, T b)
{
return MathOps<T>.Add(a, b);
}
static void Main(string[] args)
{
int x = 10;
int y = 20;
int z = Add(x, y);
My a = new My();
My b = Add(a, a);
long implementMePlz = Add<long>(1, 2);
}
}
}
I trimmed my example down to a trivial task. What I really want to do is some fairly sophisticated mathematical operations on 2- and 3-dimensional arrays of data for a CAT-scan reconstructor. But I'd like to be able to operate using either single- or double-precision floating-point values.
With C++, I could make templates that did the math using standard operators, and then instantiate them using whatever data type I liked. I assumed that C# generics would give me the same capability.
If I have to go as much trouble as you indicate, and give up using standard math operators, it will be easier just to write multiple versions of the functions.
What a disappointment! Is this really the best C# can provide?
Thanks anyway. I do appreciate the effort. Anybody else have a better idea?
Differences Between C++ Templates and C# Generics (C# Programmers Reference) C# generics do not provide the same amount of flexibility as C++ templates. For example, it is not possible to call arithmetic operators in a C# generic class, although it is possible to call user defined operators.
is the following:
Bummer!
To do this easily you can use combination of partial classes, C++ macros and preprocessor.
Create a source code for your class / functions like this one:
MyFunctions.cs.tmpl
partial class MyFunctions {
RET_TYPE DoFoo(RET_TYPE x, RET_TYPE y) {
return x+y;
}
}
Then proprocess by C++ preprocessor with correct types like a:
cl -E -D RET_TYPE=double MyFunctions.cs.tmpl > MyFuntions.double.cs
Add all thouse generated sources to project.
To make this automaticaly you can batch script that will do this as pre-build event (Project -> Properties -> Build Events)
As partial modifier was used - all functions will reside in same class and correct overloads will be selected automaticaly.
P.S> As well you can take a look on C++/CLI that will allow to generate .NET IL code with C++ syntax.
Hope this helps.
Sorry for such a compicated example.
You do with a little bit simpler with help of "using" statements:
using T = System.Double;
class MyFunctions
{
T DoFoo(T x, T y)
{
return x+y;
}
}
All you need now is to keep sources with different using statements in sync.