Problem using C# generics

I’m new to generics - I’m trying to make a class that can be used with a float or a Vector3 (eventually adding V2 and Quaternion). I had created 4 different classes, but it seemed Generics might make more sense. But I’ve hit a snag. Take the following code:

public class Gen<G> {

	public G foo;
	
	public void UpdateFoo( float f ) {
		if (foo.GetType().FullName=="System.Single") {
			foo+=0.1f;
		}
 		else if (foo.GetType().FullName=="UnityEngine.Vector3") {
			foo+=new Vector3(0.1,0.1,0.1);
		}
	}
	
}

The idea is to operate on the main variables differently, based on what type they are. But I get the following compile errors:

Operator +=' cannot be applied to operands of type G’ and `float’

Operator +=' cannot be applied to operands of type G’ and `UnityEngine.Vector3’

So does that mean that when using generics, I can’t actually operate on the variables?

When you say G, unless it’s constrained then the compiler assumes the operators of the base of the hierarchy which is System.Object. System.Object has no + operator. It does not yet know that it’s a float so it cannot compile code to that. What you really want to do is constrain the generic such that the method always knows that its “something with a + operator”. Some other languages support this type of rich constraint. Unfortunately, I don’t think C# is one of them nor does the “Single” inheritance hierarchy really help you here because of the way that the operator methods are overloaded for each type of number. As you have probably noticed there is no abstract “+” operator on Single so even if you cast it to a Single I don’t think it would work, but worth a try. Unfortunately most of the time I see the need for something like this (Spring.Net does this) where you have “some number” and want to add to it, it becomes a huge switch statement. This way they can cast to the specific type like Int32 so that C# knows how to look up the + that takes a float and an int (for instance).

Edit: Here is a post I linked for another generic question. It’s not impossible, just a pain when it should not be. I don’t want to discourage you :slight_smile: but spending time on this for little benefit will not be very fun either. The only viable solution is by Marc Gravell about half way down, but you are still left to deal with the Unity types after that.

If you come up with a great way to solve this please post it, it would be useful for everyone.

Generics work differently than c++ templates. In c++ for each version you’re using a new “version” of this class is created. Generics are compiled before you use them. The whole code have to work with with the supplied parameter.

Maybe you want something like that:

public class Gen<G>
{
    public G foo;
    public void UpdateFoo( G f )
    {
        foo += f;  // * 0.1f
    }
}

Don’t forget you can use constraints to specify of what types the parameter can be.

public class Gen<G> where G : Vector3,float

In my case above all possible parameters have to support += operator. Generics are there for performing the same task on similar types. They can’t really do different things depending on the parameter since the whole function body have to work with the supplied type :wink:

edit
Just found this answer and realised that it doesn’t make sense x).

Unfortunately all constraints have to match the supplied type. So two constraints to different types won’t work since the type parameter can only be derived from one type. Interfaces are an exception and here it makes sense to specify multiple constraints.

The only workaround is to use a switch / if-else chain like this:

public class Gen<G>
{
    public G foo;
    public void UpdateFoo( G f )
    {
        if (foo is float)
            foo = (G)((float)foo + (float)f);
        else if (foo is Vector3)
            foo = (G)((Vector3)foo + (Vector3)f);

    }
}

This is again untested, I’m not sure if all casts will work. Generics are a bit tricky.

btw the type comparison could also be done like this:

    if(typeof(G) == typeof(float))