4

I have a list of numbers, and I wrote a method that performs some calculations on these numbers; all in all, it's about a page of code. The method performs some arithmetic and comparisons on these numbers.

My problem is that, in one case, the list is an IList<byte>, and in another case, it's an IList<float>. The algorithm in both cases is exactly the same (yes, I'm aware of things like overflow errors and loss of precision, but in my case it works). How can I write a method that will handle both lists ? I can't write something like void DoStuff<T>(IList<T> numbers), because there are no arithmetic operators (+ - * /) that are generic.

One solution is to simply store everything as float, but I'd like to avoid it. The lists are quite long, and thus storing floats instead of bytes would cost too much memory. I could also do something like DoStuffFloat(byteList.Select(b => (float)b)), but I don't want to pay the performance penalty, either, if I can avoid it.

Short of copy-pasting the entire method and replacing "float" with "byte" (or vice versa), is there some decent solution ?

EDIT: I should've mentioned that I'm restricted to using .NET 3.5 for this project.

Bugmaster
  • 1,048
  • 9
  • 18
  • can u post your code? is that ok? – DarthVader Sep 13 '12 at 22:18
  • How many list items are we talking about that it would cost too much memory? – Sam Axe Sep 13 '12 at 22:19
  • Unfortunately `.NET` doesn't have a [numerical tower](http://en.wikipedia.org/wiki/Numerical_tower). This means there is no type relationship between the numeric types that would allow you to express this with generics. – millimoose Sep 13 '12 at 22:22
  • instead of storing them all as floats, how about store them all as int. Scale your floats up so you can do int math on them and then scale the result back when you get it back. – Sam Axe Sep 13 '12 at 22:29
  • I'm not sure if I am allowed to post my code or not, so I'd rather err on the side of caution, sorry. Scaling floats to fixed point int is not a bad compromise, but it would still entail wasting memory when compared to bytes. I have about 10M items per list, and I could have about 100 lists in the worst case. – Bugmaster Sep 13 '12 at 23:49
  • @Bugmaster: any thoughts on storing those lists in a database (like Sql Server) and letting that do the calculations? Memory is much less of an issue at that point as sql server is fairly efficient at memory management – Sam Axe Sep 14 '12 at 00:28
  • you should have a look at this post....one of the links includes a utility class that does what you are looking for. http://stackoverflow.com/questions/1267902/generics-where-t-is-a-number – alistair Sep 13 '12 at 23:44
  • I wonder if anyone has tried `IConvertible` and its related wrapper functions in `System.Convert`? (I'm not making any claims about performance; just trying to get the thing done.) – rwong Jun 30 '14 at 05:50

3 Answers3

6

What you could do is create a generic interface that includes the operations that you want to support, create a generic factory to create instances for the supported types to perform the operations, and use it.

e.g.,

public interface IOperations<T>
{
    T Add(T a, T b);
    T Subtract(T a, T b);
    T Multiply(T a, T b);
    T Divide(T a, T b);
}

public static class Operations<T>
{
    public static IOperations<T> Default { get { return Create(); } }

    static IOperations<T> Create()
    {
        var type = typeof(T);
        switch (Type.GetTypeCode(type))
        {
        case TypeCode.Byte:
            return (IOperations<T>)new ByteOperations();
        case TypeCode.Single:
            return (IOperations<T>)new SingleOperations();
        default:
            var message = String.Format("Operations for type {0} is not supported.", type.Name);
            throw new NotSupportedException(message);
        }
    }

    class ByteOperations : IOperations<byte>
    {
        public byte Add(byte a, byte b)      { return unchecked ((byte)(a + b)); }
        public byte Subtract(byte a, byte b) { return unchecked ((byte)(a - b)); }
        public byte Multiply(byte a, byte b) { return unchecked ((byte)(a * b)); }
        public byte Divide(byte a, byte b)   { return unchecked ((byte)(a / b)); }
    }

    class SingleOperations : IOperations<float>
    {
        public float Add(float a, float b)      { return a + b; }
        public float Subtract(float a, float b) { return a - b; }
        public float Multiply(float a, float b) { return a * b; }
        public float Divide(float a, float b)   { return a / b; }
    }
}
T Mean<T>(IList<T> numbers)
{
    var operations = Operations<T>.Default;
    var sum = numbers.Aggregate(operations.Add);
    var count = (T)Convert.ChangeType(numbers.Count, typeof(T));
    return operations.Divide(sum, count);
}

var resultByte = Mean(new byte[] { 1, 2, 3, 4 });                // 2
var resultSingle = Mean(new float[] { 1.1F, 2.1F, 3.1F, 4.1F }); // 2.6F
var resultInt = Mean(new int[] { 1, 2, 3, 4 });                  // not supported

If you don't mind a small performance hit, you could dynamically create the operations needed.

class GenericOperations<T> : IOperations<T>
{
    public GenericOperations()
    {
        add = CreateLambda(Expression.Add);
        subtract = CreateLambda(Expression.Subtract);
        multiply = CreateLambda(Expression.Multiply);
        divide = CreateLambda(Expression.Divide);
    }
    private Func<T, T, T> add, subtract, multiply, divide;
    private static Func<T, T, T> CreateLambda(Func<Expression, Expression, BinaryExpression> op)
    {
        var a = Expression.Parameter(typeof(T), "a");
        var b = Expression.Parameter(typeof(T), "b");
        var body = op(a, b);
        var expr = Expression.Lambda<Func<T, T, T>>(body, a, b);
        return expr.Compile();
    }

    public T Add(T a, T b)      { return add(a, b); }
    public T Subtract(T a, T b) { return subtract(a, b); }
    public T Multiply(T a, T b) { return multiply(a, b); }
    public T Divide(T a, T b)   { return divide(a, b); }
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
5

I don't know if this is the best method for your case but it is useful for similar cases too.

This can be done by using the dynamic keyword. What dynamic will do is it will not do the compile time checks until runtime.

Here is a small sample program to show how it works.

class Program
{
    static void Main()
    {
        List<byte> bytes = new List<byte>();
        bytes.Add(2);
        bytes.Add(1);

        List<float> floats = new List<float>();
        floats.Add(2.5F);
        floats.Add(1F);

        Console.WriteLine(DoStuff(bytes));
        Console.WriteLine(DoStuff(floats));
        Console.ReadLine();
    }

    static dynamic DoStuff(IList items)
    {
        dynamic item0 = items[0];
        dynamic item1 = items[1];
        return item0 - item1;
    }

}

Unfortunately in my quick testing I could not make IList<dynamic> work however using the non generic IList then accessing the members as a dynamic works fine.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • This would be a really sweet solution, but it looks like the `dynamic` keyword is only available in 4.0. Sadly, I'm restricted to 3.5. Bah :-( – Bugmaster Sep 13 '12 at 23:31
0

Create classes to wrap the underlying values, and have them each implement an interface with the operations you need. Then, use ILists of that interface instead of the raw values.

djs
  • 220
  • 1
  • 5
  • @Bugmaster How big of a performance hit? Have you profiled it, or are you assuming? It may be a lot less than you think. – Scott Chamberlain Sep 13 '12 at 23:46
  • If you're that concerned about the performance, just duplicate the function for each data type. Then you can try it my way and compare the performance. – djs Sep 14 '12 at 16:54