A true generic implementation must support types that don't exist in
the (base) library wrapping the code. Maybe I am overcomplicating
things, but you could do this via interfaces etc as follows. You could
add a few more "standard" implementations, but more importantly the
end-developer can hook their own definitions for complex, quoternian,
SomeClass, SomeStruct, etc...
Anyways, here it is: (this is just a demo... not production code)
using System;
using System.Collections.Generic;
using System.Text;
static class Program {
static void Main() {
Console.WriteLine(Foo(1, 4, 6));
Console.WriteLine(Foo(12.0F, 1.2F, 6F));
Console.WriteLine(Math.Add("abc","def", "ghi"));
try {
Console.WriteLine(Foo("abc", "def", "ghi"));
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
static T Foo<T>(T arg1, T arg2, T arg3) {
return (Math.Product(Math.Add(arg1, arg2), arg3));
}
}
public interface IMath<T> {
T Add(T lhs, T rhs);
T Add(params T[] args);
T Product(T lhs, T rhs);
T Product(params T[] args);
T Negate(T arg);
T Invert(T arg);
T Unit { get;}
T Zero { get;}
// etc
}
public abstract class MathBase<T> : IMath<T> {
protected T ThrowNotSupported(string name) {
throw new NotSupportedException(name + " is not supported for
" + typeof(T).FullName);
}
virtual public T Add(params T[] args) {
if (args == null) throw new ArgumentNullException("args");
IMath<T> provider = Math<T>.Default;
switch(args.Length) {
case 0: return provider.Zero;
case 1: return args[0];
case 2: return provider.Add(args[0], args[1]);
}
T value = provider.Add(args[0], args[1]);
for (int i = 2; i < args.Length; i++) {
value = provider.Add(value, args);
}
return value;
}
virtual public T Product(params T[] args) {
if (args == null) throw new ArgumentNullException("args");
IMath<T> provider = Math<T>.Default;
switch(args.Length) {
case 0: return provider.Unit;
case 1: return args[0];
case 2: return provider.Product(args[0], args[1]);
}
T value = provider.Product(args[0], args[1]);
for(int i = 2; i < args.Length ; i++) {
value = provider.Product(value, args);
}
return value;
}
virtual public T Add(T lhs, T rhs) {
return ThrowNotSupported("Add");
}
virtual public T Product(T lhs, T rhs) {
return ThrowNotSupported("Product");
}
virtual public T Negate(T arg) {
return ThrowNotSupported("Negate");
}
virtual public T Invert(T arg) {
return ThrowNotSupported("Invert");
}
virtual public T Unit {
get { return ThrowNotSupported("Unit");}
}
virtual public T Zero { // this is actually a reasonable default
implementation
get { return default(T); }
}
}
internal static class Math<T> { // avoid using the dictionary on each
access
private static readonly IMath<T> provider;
public static IMath<T> Default { get { return provider; } }
static Math() {
provider = Math.GetHandler<T>();
}
}
public static class Math {
public static T Add<T>(T lhs, T rhs) {
return Math<T>.Default.Add(lhs, rhs);
}
public static T Add<T>(params T[] args) {
return Math<T>.Default.Add(args);
}
public static T Product<T>(T lhs, T rhs) {
return Math<T>.Default.Product(lhs, rhs);
}
public static T Product<T>(params T[] args) {
return Math<T>.Default.Product(args);
}
public static T Negate<T>(T arg) {
return Math<T>.Default.Negate(arg);
}
public static T Invert<T>(T arg) {
return Math<T>.Default.Invert(arg);
}
public static T Unit<T>() {
return Math<T>.Default.Unit;
}
public static T Zero<T>() {
return Math<T>.Default.Zero;
}
static Math() {
providers = new Dictionary<Type, object>();
AddProvider<int>(new MathInt32Provider());
AddProvider<float>(new MathSingleProvider());
AddProvider<string>(new MathStringProvider());
}
internal sealed class MathInt32Provider : MathBase<int> {
override public int Add(int lhs, int rhs) {return lhs + rhs;}
override public int Product(int lhs, int rhs) { return lhs *
rhs; }
override public int Negate(int arg) { return -arg; }
override public int Invert(int arg) { throw new
NotSupportedException(); }
override public int Unit { get { return 1; } }
}
internal sealed class MathSingleProvider : MathBase<float> {
override public float Add(float lhs, float rhs) { return lhs +
rhs; }
override public float Product(float lhs, float rhs) { return
lhs * rhs; }
override public float Negate(float arg) { return -arg; }
override public float Invert(float arg) { return 1.0F / arg; }
override public float Unit { get { return 1.0F; } }
}
internal sealed class MathStringProvider : MathBase<string> {
override public string Add(string lhs, string rhs) { return
lhs + rhs; }
override public string Zero { get { return ""; } }
public override string Add(params string[] args) {
if (args == null) throw new ArgumentNullException("args");
switch (args.Length) {
case 0: return "";
case 1: return args[0];
case 2: return string.Concat(args[0], args[1]);
case 3: return string.Concat(args[0], args[1],
args[2]);
case 4: return string.Concat(args[0], args[1],
args[2], args[3]);
}
StringBuilder sb = new StringBuilder();
foreach (string arg in args) {
sb.Append(arg);
}
return sb.ToString();
}
}
static readonly Dictionary<Type, object> providers;
public static void AddProvider<T>(IMath<T> provider) {
if(provider==null) throw new
ArgumentNullException("provider");
lock (provider) {
providers.Add(typeof(T), provider);
}
}
internal static IMath<T> GetHandler<T>() {
lock (providers) {
object provider;
if (!providers.TryGetValue(typeof(T), out provider)) {
throw new NotSupportedException("No IMath<T> provider
exists for " + typeof(T).FullName);
}
return (IMath<T>)provider; // cast is OK since it made it
in
}
}
}