13

I'm programming in Unity 3.4.2 on OS X using C#.

I have a class like the following:

class Foo<T>
{
    public T DoFoo(T bar)
    {
        float aFloatValue = 1.0f;
        // Do other stuff...
        return aFloatValue * bar;
    }
}

When Unity compiles this class, it gives me this error message:

error CS0019: Operator *' cannot be applied to operands of type float' and `T'

I know that the types I provide for T will support multiplication with float. How can I implement generic multiplication in this case?

nathanchere
  • 8,008
  • 15
  • 65
  • 86
Troy J. Farrell
  • 1,252
  • 11
  • 18
  • Related to http://stackoverflow.com/questions/63694/creating-a-math-library-using-generics-in-c-sharp and http://stackoverflow.com/questions/814202/how-to-implement-generic-method-to-do-math-calculations-on-different-value-types – Ben Voigt Feb 28 '12 at 17:02
  • Ben, thanks for those links. My problem is related, but not quite the same. Some of the types I'm interested in using are provided by Unity and can't be subclassed. At the moment, those are UnityEngine.Vector2 and UnityEngine.Vector3. – Troy J. Farrell Feb 28 '12 at 17:18

5 Answers5

11

Ahhh, good ol' Haskell.

You can't do that in C#, you should have multiple DoFoo's, one for float, one for double and one for decimal - there aren't all that many float types. You can drop the float variant, too, as it will be implicitly cast into a double anyway.

zmbq
  • 38,013
  • 14
  • 101
  • 171
  • 2
    Despite the best efforts of everyone else, for my very specific use case (a few types, Unity 3.4.2 and limited experience with C#), this ends up being the most useful advice. – Troy J. Farrell Feb 28 '12 at 18:32
8

It's not possible to do this with generics alone as they don't support operators such as +, /, -, *. To do this you'll need to introduce a layer of abstraction in the form of say an interface or a lambda to provide the operation.

For example

class Foo<T> {
  Func<T, float, T> _multiplyFunc;
  public Foo(Func<T, float, T> multiplyFunc) {
    _multiplyFunc = multiplyFunc;
  }
  public T DoFoo(T bar) {
    float aFloatValue = 1.0f;
    return _multiplyFunc(bar, aFloatValue);
  }
}

Now at construction time of Foo<T> you can tell it how to multiply with a float type

var f = new Foo<MyType>((x, y) => x * y);
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • The version of Mono baked into Unity 3.4.2 doesn't support lambda functions, so this may be possible, but also cumbersome. – Troy J. Farrell Feb 28 '12 at 17:14
  • 2
    @TroyJ.Farrell instead of a lambda expression, pass a static function. Declare `public static MultiplyVector2(Vector2 a, float b) { return a * b; }` (and similar functions for other types you'll need) and then call `var f = new Foo(Foo.MultiplyVector2);`. You could also create a factory method to avoid leaking the implementation detail of needing to supply a multiplier function. The best solution depends on how you want the call site to look, which you haven't told us. – phoog Feb 28 '12 at 17:43
  • Thanks for that insight. I haven't described the call site because I didn't really see it as material to the answer. I'd rather not be passing around the multiplication function, so a factory could mask that. I'm actually already using a factory, so that fits well. – Troy J. Farrell Feb 28 '12 at 17:59
6

In C# 4, you can use dynamic if you are confident that float * T => T.

class Foo<T>
{
    public T DoFoo(T bar)
    {
        dynamic aFloatValue = 1.0f;
        // Do other stuff...
        return aFloatValue * bar;
    }
}

Other options are:

  1. Use an expression-tree and compile it down to delegate (suitable for caching) that does the multiplication for you.
  2. Reflection - Either directly, or by producing a delegate first.
  3. Accept a delegate, as JaredPar mentions.
Ani
  • 111,048
  • 26
  • 262
  • 307
  • The real issue here is that the version of Mono baked into Unity 3.4.2 doesn't support C# 4. I'll keep this in mind when I'm on the newer runtime. – Troy J. Farrell Feb 28 '12 at 16:50
2

try this

class Foo<T>
{
    public T DoFoo(T bar)
    {
        float aFloatValue = 1.0f;
        var barValue = bar as dynamic;
        return aFloatValue * bar;
    }
}

it should've work, no errors encountered yet...

GaaRa
  • 520
  • 6
  • 21
2

Since you say dynamic is not an option: if you obtain MiscUtil, I wrote some support in there for operators on generics. In particular, look at Operator.Multiply and Operator.MultiplyAlternative, discussed here. This resolves the methods at runtime, baking them into delegates as needed.

This uses the Expression API, so will work on 3.5, but if needed I could reproduce it for 2.0 using ILGenerator

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900