Re: Generic methods..

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



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[i]);
}
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[i]);
}
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
}
}
}


.



Relevant Pages

  • Re: why is it so ?
    ... >>> (lhs, rhs) ... > Could you please tell me by which rule you have interleaved evaluation ... method of breaking up expressions using partial ordering by creating an ...
    (comp.lang.c)
  • Re: Suggestion for C# language: Modification to the "new" keyword - Allow type inference in C# t
    ... I can't think of a situation where info flows from the RHS into LHS before the operation on the LHS can complete. ... However, using your analogy, it could be said that there are situations where the LHS participates in the equation independently from the RHS. ... The introduction of "var" was radically different, in that the barrier which kept each side from influencing the other was broken down. ... Can you think of any other statement or expression where info flows from the RHS into LHS before the operation on the LHS can complete? ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Suggestion for C# language: Modification to the "new" keyword - Allow type inference in C# t
    ... The RHS is no longer independant of the LHS and that is just ... Why oh why must you write MyClass TWICE: ... IF this is an assignment statement ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: A good ride, ruined
    ... straight into it, on the LHS, knocking it over onto its RHS. ... always, the superficial damager didn't ...
    (uk.rec.motorcycles)
  • Re: Slight problem using user-defined types with STL sets
    ... > I've got a fairly basic question about STL sets and operator overloading ... bool operator<(const twotuple& lhs, const twotuple& rhs) ... rhs are equivalent with respect to the first coordinate but lhs is less than rhs ...
    (comp.lang.cpp)