Operator '+' cannot be applied to operands of type 'T' and 'T'

One day, the Microsoft C# team will stop treat us as kids, and this code will become possible:

< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

public static ABIDataVoidNull<T> op_Addition_NV_NV<T>(ABIDataVoidNull<T> inValueL, ABIDataVoidNull<T> inValueR)

       {

           if (inValueL.IsVoid)

           {

               return (ABIDataVoidNull<T>)inValueR;

           }

           else if (inValueR.IsVoid)

           {

               return (ABIDataVoidNull<T>)inValueL;

           }

           else if (inValueL.IsNull)

           {

               return (ABIDataVoidNull<T>)inValueR;

           }

           else if (inValueR.IsNull)

           {

               return (ABIDataVoidNull<T>)inValueL;

           }

           else

           {

               return (ABIDataVoidNull<T>) (inValueL.Value + inValueR.Value);

           }

       }

 

 

Currently it cannot compile for safety I think, and I will understand that if the code was VB.NET, you can treat the VB guys as kids, I don’t care, I don’t see why shouldn’t compile in C#

 

I can mark my class as “unsafe”, I just wanted it to compile, and if T does not support +, trigger an exception, I will make sure it does not happen.

 

I think it is my decision to have the code unsafe.

 

Shouldn’t C# be a bit more like C++, relaxed and powerful



Answer this question

Operator '+' cannot be applied to operands of type 'T' and 'T'

  • Chris Lively

    operator + is defined as a static function, and you can't access any static functions of a type parameter. Definately one of my three major frustrations with Generics vs. Templates (other two being no specialization and no template template parameters).

    Maybe someday we'll get static functionality, there must be a good reason (presumably CLR-based) that you can't.

  • hye_heena

    I guess guys from C# are quite right not allowing you to write code that may (and probably will) cause bugs.
    As I understood from your code ABIDataVoidNull<T>.Value property is of type T. If compiler doesn't know anything about T than:

    1. It doesn't know how to add values of type T.
    2. It doesn't know the result type of addition.
    3. It doesn't know how to convert unknown result type to ABIDataVoidNull<T>.

    BUT. Look at the following code, it will compile without errors, strongly typed and works as expected:

    public class MyType
    {
       //just example: result of summing MyTypes is bool.
         
    public static bool operator +(MyType a, MyType b)
          {
                //just a dummy return
                return false;
          }
    }

    public
    class cMyType : MyType { }

    class MyGeneric<T> where T : MyType
    {
          T _value;
          public T Value
     
        
                get { return _value; } 
          }

          //converting bool to MyGeneric
          public static explicit operator MyGeneric<T>(bool a)
          {
                //just a dummy return
                return new MyGeneric<T>();
          }
    < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 
          public static MyGeneric<T> operator +(MyGeneric<T> a, MyGeneric<T> b)
          {
                //note that "+" will always call MyType.operator+
                //even if it is redeclared in T.
               
    return (MyGeneric<T>)(a.Value + b.Value);
          }
    }

    ...

    //somewhere in your code:
    MyGeneric<cMyType> aaa = new MyGeneric<cMyType>();
    MyGeneric<cMyType> bbb = new MyGeneric<cMyType>();
    aaa = aaa + bbb;


  • Dudley

    But it's the only way you can get something like this to work. Ignoring the fact that operators are static functions and so can't make use of late binding, how do you propose the compiler decides how it should add two instances of T, given that, as far as it knows, T is just an Object It would have to examine T.GetType, and try and find a function with a matching signature - id est, use Reflection - as looking up the function in the Virtual Function Lookup table is not possible without knowing the object's type.

    Why should this breech of type-safety be formalised, and not any other

  • singlenipple

    In reading this I see two points. The first is how to make this example work; that has been well argued in the other postings. The second is why is C# implementation of generics more restrictive than C++. Unlike C++, .NET generics are resolved at compile time rather than runtime. It is my understanding that the compiler creates types for you (under the covers) for each implementation of your generic class. I guess that the compiler could be a little less restrictive and throw an error only if the type that you are trying to use does not implement '+'. I would surmise that our reliance on intellisens probably drove this decision as much as anything. Keep in mind that the .NET Framework was designed to be all things to all developers writing applications for the Windows platform. Unfortunately, some decisions were made to keep the VB guys happy and strip some of the elegance we all enjoyed back in the C++ days -- don't even get me started on the dev changes made in ASP.NET 2.0.

    JPB



  • Mile Petrov

    KeeperOC wrote:
    I can see problems with that implementation, though I agree that's the closest to a sensible formal solution.

    How would you declare the members of the interface NumericValue Assume we're declaring an "Add" function, and the static operators call that function, would you declare "Add(Object operand)" or "Add(int operand)", "Add(double operand)", etc.

    The first case I can see working, though it requires a lot of internal type-checking - the second makes better use of late-binding, but raises issues regarding return types - int.Add(int operand) should return an int, whilst double.Add(int operand) should return a double.

    Andrew

    Maybe there was a misunderstanding. I was talking from .NET designer perspective. I mean that .NET SHOULD BE equipped with a special ValueType named "NumericValueType".

    This new type, by definition, should support relational and algebraic operators and internally treated as a ValueType.

    So we could get the performance benefits that we require to a value type and the required support at compile time to allow the constructed that in general, for a "generic" (not in technical sense in this context) type, is not allowed.

    In other words, what I've baptisized "System.NumeriValueType" should be an internal ValueType marker resolved at compile time by the GenericCompiler module.

    The solution I've chosed, in the practice, taking into consideration the size of the class library I was building and the stability of the exposed types (by design), is a bit code intensive (just a bit) but I avoided any kind of box/unboxing or even worse, reflection that would be too expensive at runtime.


  • JonnyAJAX

    I think that a more elegant way to fix the problem is to define a hierarchy for numeric types. All the numeric types could implement, not only + operator and the rest ones (-, *, / etc), but even relational comparer (<, <=, >, >=).

    I was attempting to do something like this

    internal abstract class DioNumericItem<T> : DioValueItem<T> where T:struct, IComparable<T>

    {

    private T _MinValue;

    private T _MaxValue;

    public override bool Validate()

    {

    if (_MinValue.HasValue && _Value <= _MinValue.Value)

    _IsValid = false;

    if (_IsValid && _MaxValue.HasValue && _Value >= _MaxValue.Value)

    _IsValid = false;

    return _IsValid && base.Validate();

    }

    }

    And I get a stupid error such as:

    Error 1 Operator '<=' cannot be applied to operands of type 'T' and 'T' 


  • 0xDEADBEEF

    If you absolutely must work around the type-safety, why not just use Reflection Then you can call any function you like, and if it isn't there, you'll get an exception thrown.

    Andrew

  • Princila

    I think we're discussing from a "formal" point of view on this topic.

    Reflection is just a way to make the things works but it's very ugly in terms of programming-experience.


  • killfr0g

    Yeah, painful.

    < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

    You have to define your own Add method (or whatever you want to call it) for the T, and then define the operator as a call to that method.

     

    Helpful discussion in C# Precisely



  • enric vives

    As I've already stated some post above, I propose to introduce a "Numeric hierarchy" under the System.ValueType.

    A "NumericValue" type is expected to implement math operators and relational operations too.

    I could declare something such as:

    public class MyClass<T>  where T : System.NumericValueType  (Nice Name! )

    This is from a "formal" point of view, not taking in consideration of the present constraints imposed by the CLR on ValueTypes and their treatment.

    I think it should be very elegant and gains all the needs to the compiler to manage the math operators.


  • VBAddict

    It would be quite useful to be able to constrain a generic type by not only its interface, but also the operators it supports. I'm unsure what the syntax could be, but something like this:

    public class MyMathClass<T> where T: +(T, T) { //... }

    Now I can do T + T inside MyMathClass. Currently this is possible but requires lots of typing; I need to define an interface that exposes the desired operators and constrain T to implement that.

    I'm sure the C# team has toyed with this idea



  • Viktor78

    You have it backwards.  C++ generates the templated types as they are used at compile time.  This allows it be more flexible because everything is known at compile time.  The generic code for a C# type is emitted by the compiler, but is not bound to specific types until runtime.  Therefore, the compiler does not know what types the generic type may be bound to, and it has to be more restrictive to prevent the execution of bad code at runtime.  Part of the reason for this different between C++ and C# is that types don't exist at runtime in C++ (unless you're using some special libraries and even then they only exist in a limited form).  So, in C++, you can get away with the fact that vector<> isn't really a type, it is simply a definition that exists at compile time to which type checking rules don't apply.  The type checking rules only apply to the types constructed from that template definition.  In C#, however, List<> is a full-fledged type, and so all the type checking rules apply to it before its bound to a type.


  • WinFormsUser13232

    Thank you for the sample, but how can you say when T is int or long

    < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 

    And about the generic, the compiler can simply trigger an error (in this case) when the generic is used with something that not support the + operation, not on the generic itself.

     

    Anyway, in C# casting, if not used correctly can cause bug, unsafe code can cause bugs, a business logic not implemented as it should will cause bugs.

     

    I know the compiler is trying to help, but maybe some of the restrictions can be relaxed, like me marking the class “unsafe” for example, and the compiler allowing me to pass some of the restrictions, unless for sure the code will brake.

     

    At the end, isn’t the developer the one that should be deciding

     


  • Mystagogue

    I can see problems with that implementation, though I agree that's the closest to a sensible formal solution.

    How would you declare the members of the interface NumericValue Assume we're declaring an "Add" function, and the static operators call that function, would you declare "Add(Object operand)" or "Add(int operand)", "Add(double operand)", etc.

    The first case I can see working, though it requires a lot of internal type-checking - the second makes better use of late-binding, but raises issues regarding return types - int.Add(int operand) should return an int, whilst double.Add(int operand) should return a double.

    Andrew

  • Operator '+' cannot be applied to operands of type 'T' and 'T'