25

When trying to call this function in my code i get the error in the title. Also Operator '+=' cannot be applied to the operands of type 'int' and 'T'

public int Change<T>(Stats type, T value)
    {
        Dictionary<string, string> temp = new Dictionary<string, string>();
        temp = sql.Query(string.Format("SELECT {0} FROM player WHERE fbId='{1}'", type.ToString(), FBId));
        if (typeof(T) == typeof(int))
        {
            int t = Convert.ToInt16(temp[type.ToString()]);
            t += value;
            if (t < 0) return -1;
            PlayerStats[type] = t;

        }
        sql.Upload(string.Format("UPDATE player SET {0}='{1}' WHERE fbId='{2}'", type.ToString(), PlayerStats[type], FBId));
        return 0;
    }

I call the function by using:

Change<int>(type, 1);
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
David W
  • 1,833
  • 5
  • 21
  • 25
  • 16
    What is the point of making this method generic if it's not generic? – dlev Nov 17 '11 at 16:37
  • 5
    How confident are you that Bobby Tables isn't going to wreck your database? – Anthony Pegram Nov 17 '11 at 16:40
  • 1
    I would do something about your SQL interface... that seems like an invitation to an injection attack. – Jeremy Holovacs Nov 17 '11 at 16:41
  • 6
    I notice that your "question" does not actually contain a question; rather, it contains a description of correct compiler behaviour. **Did you have a question?** Based on your code I suspect that you believe that C# generics are a kind of C++ template. They are not. A C++ template is *recompiled* every time you construct it, and therefore only needs to be correct for *the actual constructions*. A C# generic is actually *generic*; it is required to be correct for *any possible construction*, not just *the constructions you actually make*. An arbitrary T cannot be added to an int. – Eric Lippert Nov 17 '11 at 16:55
  • possible duplicate of [Cannot implicitly convert type 'Int' to 'T'](http://stackoverflow.com/questions/8171412/cannot-implicitly-convert-type-int-to-t) – nawfal May 07 '13 at 04:08

6 Answers6

25

you can try casting the value like this ...

t += (int)value; 

or

t+= Convert.ToInt32(value);
Glory Raj
  • 17,397
  • 27
  • 100
  • 203
16

Or another way (object cast is necessary not typo)

t += (int)(object)value;

Or use dynamic, by using dynamic you can do more, such as implicit casts

Or use Int32 - Int32 and int are both struct internally. No performance loss

Bamboo
  • 2,046
  • 2
  • 16
  • 21
  • 1
    Not sure if it is good idea to add boxing/unboxing transformations to generic function which using integer explicitly.. – Jviaches Jul 20 '17 at 04:14
  • @Jviaches It will work provided the runtime type of `value` is **exactly** `Int32`. But you cannot directly cast a boxed `Int64` to `Int32`, for example, so this will fail: `(Int32)(Object)(someInt64Value)`. But this will work: `(Int32)(Object)(Int32)(someInt64Value)` - which kinda defeats the point of boxing in the first place. But if the leftmost `Int32` is a generic `T` then you'll need to do it manually by testing with `typeof(T) == typeof(Int32)`, ugh. – Dai Oct 10 '20 at 01:22
9

You can set constraint:

public int Change<T>(Stats type, T value) where T : IConvertible

Then:

var intValue = value.ToInt32();
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
  • I believe by-default this will cause `value` to be boxed, but if you add a `where T : struct, IConvertible` constraint then the CSC/JIT won't box `value`. – Dai Oct 10 '20 at 01:23
2

I had a very similar problem. It is not exactly the problem asked here, but it could be a start point:

    private T GetXValue<T>(XElement xElem, string name)
    {
        try
        {
            object o = xElem.Element(name).Value;
            return (T)Convert.ChangeType(o, typeof(T));
        }
        catch 
        {
            return (T)Activator.CreateInstance(typeof(T), null);
        }
    }
Rogério Silva
  • 121
  • 1
  • 11
2

It knows not how to add your T to a numeric since it doesnt know what the type of T is going to be.

t += Convert.ToInt32(value);

But since you are adding int to int and returning int then why not just ditch the generic parameter and make it

public int Change(Stats type, int value)  

and if you want different behaviour for different types and really want the same method name, instead of testing the type just do:

 public int Change(Stats type, string value) 
 public int Change(Stats type, DateTime value)  
Richard Friend
  • 15,800
  • 1
  • 42
  • 60
  • Because i might pass an int, a bool, a string. It all varies on when i need to Change(); – David W Nov 17 '11 at 16:42
  • @David, the suggestion is valid. So far, you have shown us custom logic you want to perform for an integer. At the very least, you could consider having `Change(Stats type, int value)` and `Change(Stats type, T value)` overloads. Generics are useful when the types do not matter for the operation. You have shown us one instance where the type *does* matter. – Anthony Pegram Nov 17 '11 at 16:49
  • You are only using the generic type to define the methods logic/flow, may as well create some overloads, at least you will get a compiler check you havent passed in a type that is invalid, you wont be able to rely on generic type constraints for that if you are opening it up to all the types you mention. – Richard Friend Nov 17 '11 at 16:52
0

The error you are getting makes sence. While you call the method with int as the type, the compiler doesn't know that will be the case.

To compile the method as it is, the compiler will need to prove that the operations you do on T will be valid for all T - which is clearly not the case.

driis
  • 161,458
  • 45
  • 265
  • 341