1

I have an Interface called "IOperation"

interface IOperation<T,U>
{
    T FirstOperand { get; set; }
    U SecondOperand { get; set; }
    int Result { get; }
}

FirstOperand and SecondOperand can either be an int or another IOperation. What is the best way to implement such behaviour? Example of an inherited class:

class Subtraction<T, U> : IOperation<T, U>
{
    public T FirstOperand { get; set; }
    public U SecondOperand { get; set; }

    public int Result
    {
        get
        {
            var first = 0;
            var second = 0;
            if(FirstOperand is Double)
            {
                first = (Double) FirstOperand;
            }
            if(FirstOperand is IOperation)
            {
                first = (IOperation) FirstOperand;
            }
            // TODO: Same thing with SecondOperand

            return FirstOperand - SecondOperand;
        }
    }
}

Well, compilation error, apparently a direct cast doesn't work for generics. This problem could be fixed by casting into object first, but I'm pretty sure that would be a terrible approach and not good practice at all. Do you have any suggestions how I can solve this problem in a good way?

Damien Flury
  • 769
  • 10
  • 23
  • *"apparently a direct cast doesn't work for generics"* -- it works if the constraints on the type parameter guarantee that it'll work. – 15ee8f99-57ff-4f92-890c-b56153 Jun 06 '17 at 17:22
  • How can I guarantee that? I can't let int inherit from an own interface... – Damien Flury Jun 06 '17 at 17:23
  • Well, unfortunately you cannot really directly constrain a generic type parameter to a numeric type only. But there are possible workarounds, look here: https://stackoverflow.com/questions/32664/is-there-a-constraint-that-restricts-my-generic-method-to-numeric-types (even offering a nuget package...) –  Jun 06 '17 at 17:26
  • 1
    I would make the operand type something that can have a value that is either `IOperation` or `int`. Maybe just write a quickie class that implements `IOperation` and `int Result` returns an internal field. I mean, if `3` is a valid operand, write a class that represents it as such. I don't think there's any way to use constraints to say "Either IOperation or int" -- not in C#. – 15ee8f99-57ff-4f92-890c-b56153 Jun 06 '17 at 17:27
  • Is `Result` *necessarily* an `int`? – 15ee8f99-57ff-4f92-890c-b56153 Jun 06 '17 at 17:32
  • Unfortunately, you can't constrain a type to an int. So there is no way to guarantee this, and generics require that all methods (even if they logically cannot be accessed by that type) have working code. Further, the whole point of a generic is type safety, and since your class is checking type you're essentially cancelling out the purpose of a generic. – Erik Funkenbusch Jun 06 '17 at 17:32
  • How is this being used? Are you writing a generalized expression thing? – 15ee8f99-57ff-4f92-890c-b56153 Jun 06 '17 at 17:36
  • Little side note: casting both operands to `object` will not work, because there is no overload of the `operator -` for `object` and `object`. – Timo Jun 06 '17 at 17:57
  • This can be handful: http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html http://www.yoda.arachsys.com/csharp/miscutil/ – MistyK Jun 06 '17 at 17:59

1 Answers1

0

There is a way you could do it (I don't recommend it though).

You could pack both of your operands in a dynamic object. This will compile:

 class Subtraction<T, U> : IOperation<T, U>
 {
     public T FirstOperand { get; set; }
     public U SecondOperand { get; set; }

     public int Result
     {
         get
         {
             dynamic first = FirstOperand;
             dynamic second = SecondOperand;            
             return (int)(first - second);
         }
     }
 }

And it will work for types that have an operator - overload for the according types (i.e. int and double) and that have an implicit or explicit cast to int.

However, using dynamic objects has two major problems:

  1. type safety: this code will compile regardless of the actual types of T and U. If the types are not compatible (they don't have a common operator), you will get an exception at runtime.

  2. performance: dynamic objetcs build expression trees that are compiled on first use and than cached at runtime. This means, that your program will have to compile things at runtime which ends in slowing down your code (however, only once for each type combination that is used).

For additional information read the article about dynamic and expression trees

Timo
  • 9,269
  • 2
  • 28
  • 58
  • Thanks for your answer. But I don't like using dynamic if it's not absolutely necessary, because of the problems you listed above. I'll try to work around that problem by using a small class, of which an instance can be returned, should also make some things easier... – Damien Flury Jun 06 '17 at 19:21
  • Unfortunately, I'm not aware of a different solution that wouldn't inherit the problems I listed above. Could you explain your idea in more detail? I'm also interested in an different approach. – Timo Jun 06 '17 at 19:37
  • The idea of this approach actually came from a simple calculator-application. Well, I could have done smth similar using string parsing etc. For example hitting btn1, btnPlus, btn1 and btnEquals after each other just returns the string "1 + 1". Well I found the idea of operation -> String -> operation quite weird and I wasn't happy with that. So I've got the idea of making own "Expressions" like new Addition(1, new Subtraction(2,3)) for example. Well after all, simple methods for operations probably would have been easier. – Damien Flury Jun 06 '17 at 21:21
  • But if I later want to implement derivatives or integrals, for example, treating an operation as an object is probably a better approach. Well, my solution after all is a wrapper class for int (actually now double, which makes more sense of course) which inherits from the same interface as the operations (made an extra Interface IOperable). It's not a perfect solution, but it works and I guess I'm happy with it :) – Damien Flury Jun 06 '17 at 21:23
  • Well, for basic calculation purposes I would go with dynamic. The performance loss is not that hughe that you would notice it in such applications. But I'm not sure about C# beeing a good language for advanced mathematics. I've tried it once but I stopped working on it very early because I couldn't find a neat solution on how to model the core. Meanwhile I think - as soon as I have enough time to do it - I'm going to write my own interpreter for mathematical expressions. – Timo Jun 07 '17 at 16:26