views:

143

answers:

2

I have a problem I’m working on, currently it is working for ints but i want it to work for all classes that can be added using the + operator. Is there any way to define this in the generic? For example,

public List<T> Foo<T>() where T : ISummable

Is there any way to do this?

EDIT:
Performance of passing in a delegate to do the summing instead of using += with a type of Int was 540% slower at best. Investigating possible other solution

Final solution:
Thank you all for your suggestions. I ended up with a solution which is not too slow and enforces the checking at compile time. I cannot take full credit as a colleague helped me arrive at this. Anyway, here it is:

Implement an interface with all of the required operators on it in the form of functions

public interface IFoo<InputType, OutputType>
{
    //Adds A to B and returns a value of type OutputType
    OutputType Add(InputType a, InputType b);
    //Subtracts A from B and returns a value of type OutputType
    OutputType Subtract(InputType a, InputType b);
}

Create the class you want to define, but instead of the Where clause, use a dependency injected instance of the IFoo interface. the OutputType will most often be double because the nature of the operations is mathematical.

public class Bar<T>
{
    private readonly IFoo<T,double> _operators;

    public Bar(IFoo<T, double> operators)
    {
        _operators = operators;
    }
}

now when you use this class, you define the rules for operation like this:

private class Foo : IFoo<int, double>
{
    public double Add(int a, int b)
    {
        return (double)(a+b);
    }
    public double Subtract(int a, int b)
    {
        return (double)(a-b);
    }
}

then you would use it like this:

Foo inttoDoubleOperations = new Foo();
Bar myClass = new Bar(Foo);

that way all the operations are enforced at compile time :)

enjoy!

+1  A: 

In C# 4.0 is new keyword dynamic that allow you to perform this at runtime. In previous versions it was possible but too shady and tricky for me. But you can allways pass a delegate that will perform additions inside generic. Otherwise it is not possible because there is no ISummable, IAdditive and generally speaking no way to know is something is additive* at compilation time. If you wish some further comments I will add it later. BR.

  • I mean, except for making you own IAdditive and mark some types with them, but It won't work for example with ints.
Bart
Thanks for your reply! You hit the nail right on the head with your comment about IAdditive. I cant work it for ints and other pre-implemented classes/structs
TerrorAustralis
+5  A: 

This is a pretty commonly requested new feature for C#: the ability to specify more generic parameter constraints than the ones we already have. Operators are among the most frequently asked. However, C# does not currently support this.

Possible workarounds:

  • Pass a delegate to any method that needs to do addition. This is the most type-safe option, but of course it’s annoying if you need to call such a method often. For example:

    public class Generic<T> {
        public void DoSomething(T anItem, T anotherItem, Func<T, T, T> add) {
            // instead of
            Blah(anItem + anotherItem);
            // have to write:
            Blah(add(anItem, anotherItem));
        }
    }
    
    
    Generic<int> genInt = ...;
    // and then instead of ...
    genInt.DoSomething(1, 2);
    // have to write:
    genInt.DoSomething(1, 2, (a, b) => a + b);
    
  • Declare your own interface IAddable. Then you can use it as a generic type parameter constraint, but obviously you can’t use int as the parameter then. You would have to use a struct of your own that contains just an int and which implements IAddable:

    public interface IAddable<T> {
        T Add(T other);
    }
     
    public struct Integer : IAddable<Integer> {
        public int Value;
        public Integer(int value) { Value = value; }
        public Integer Add(Integer other) { return new Integer(Value + other.Value); }
    }
    
    
    // then instead of
    Generic<int> blah = ...;
    // have to write:
    Generic<Integer> blah = ...;
    
  • dynamic. Another possible workaround is to use dynamic, but this is rather hacky and completely unsafe: it will let you pass in any type and call any method or operator, and only crash at runtime, not at compile-time.

Timwi
Thanks, i may end up going with the passing a delegate approach. It seems like a reasonable workaround and wont add too much effort on the users part. Thanks mate!
TerrorAustralis