im only using c# 2.0
I had got part way to making a struct/interface wich wrapped all
the operators needed, but decided that generating code for the clases
its used on would be preferable, as its likly that only one of the many
diferent types would be used in any one project anyway.
Indeed. Well, let me know if you want a copy of the .NET 2.0 version -
but otherwise the interface apprach can be quite useful if you only
need to cope with known types (the version I have will work even with
custom structs).
Anyway - a simplified (few operators, few supported types) version of
the interface approach might look like below; note I've used a static
wrapper class in the middle (Calc) with generic methods; this means
you don't need to keep passing ICalc<T> instance around, and type-
inference makes calling simple (i.e. note no explicit generics
mentioned in the body of Test<T>)
using System;
static class Program
{
static void Main()
{
int x = 5, y = 6;
int z = Test(x, y);
}
static T Test<T>(T x, T y)
{
T val = Calc.Add(x, y);
val = Calc.Multiply(x, Calc.Negate(val));
return val;
}
}
public static class Calc
{
static Calc()
{
// known types
Register<int, CalcInt32>();
Register<float, CalcSingle>();
}
static void Register<TValue, TCalc>()
where TValue : struct
where TCalc : ICalc<TValue>, ICalc<TValue?>, new()
{
// handle both T and T?
TCalc calc = new TCalc();
CalcCache<TValue>.Instance = calc;
CalcCache<TValue?>.Instance = calc;
}
public static T Add<T>(T x, T y) {
return CalcCache<T>.Instance.Add(x, y);
}
public static T Multiply<T>(T x, T y)
{
return CalcCache<T>.Instance.Multiply(x, y);
}
public static T Negate<T>(T x)
{
return CalcCache<T>.Instance.Negate(x);
}
static class CalcCache<T>
{
private static ICalc<T> instance = new CalcNotSupported<T>();
public static ICalc<T> Instance
{
get
{
return instance;
}
set
{
if (instance == null) throw new
ArgumentNullException("Instance");
instance = value;
}
}
}
}
interface ICalc<T>
{
T Add(T x, T y);
T Multiply(T x, T y);
T Negate(T x);
// etc
}
sealed class CalcNotSupported<T> : ICalc<T>
{
public T Add(T x, T y) { throw new NotSupportedException(); }
public T Multiply(T x, T y) { throw new NotSupportedException(); }
public T Negate(T x) { throw new NotSupportedException(); }
}
sealed class CalcInt32 : ICalc<Int32>, ICalc<Int32?>
{
public int Add(int x, int y) { return x + y; }
public int Multiply(int x, int y) {return x * y;}
public int Negate(int x) { return -x; }
public int? Add(int? x, int? y) { return x + y; }
public int? Multiply(int? x, int? y) { return x * y; }
public int? Negate(int? x) { return -x; }
}
sealed class CalcSingle : ICalc<Single>, ICalc<Single?>
{
public float Add(float x, float y) { return x + y; }
public float Multiply(float x, float y) { return x * y; }
public float Negate(float x) { return -x; }
public float? Add(float? x, float? y) { return x + y; }
public float? Multiply(float? x, float? y) { return x * y; }
public float? Negate(float? x) { return -x; }
}