Operator Overloading with Generics - Using Inheritance to Allow Use of C# Operators (C# 2005)






3.70/5 (9 votes)
An article on how to use C# operators on the parameter types in your generic code (normally the compiler does not allow this). This solution just uses inheritance, so is fairly easy to maintain.
Introduction
This is an article about operator overloading with generics using inheritance to allow the use of C# operators (C# 2005). Normally, the C# compiler does not allow us to use C# operators on a type parameter. For example, say you have some generic code, and you are doing some processing, such as adding the types, then it would be nice to use the '+
' operator.
class CMyGenericClass<T>
{
public T TestUsingOperatorsOnT(T _one, T _two)
{
T tTemp = (_one + _two);
return tTemp;
}
}
Compiling this code results in the compiler error:
Operator '+' cannot be applied to operands of type 'T' and 'T'
Now, there may be good reasons why C# 2005 does not allow operations to be used on generic parameters. The most likely reason is that operator code is not truly 'generic'. However, there may be cases where it would be nice to use an operator...
Background
This article was inspired by the book "Pro C#2005 and the .NET 2.0 Platform", by Andrew Troelsen (an excellent book...), where I learned about the compiler restriction (that generic code could not use operators on the parameter type).
This seemed a bit restrictive to me, so I figured there must be a way around it. This is a fairly simple way around it.
The Code
We can circumvent this restriction, by creating an abstract
type, which has the desired operators. The actual implementation of the operator will be an abstract
method, which our type 'T
' must provide:
abstract class CMyOperations
{
...
public abstract CMyOperations AddMyOperations(CMyOperations _ITwo);
//the operator+, which we want to be able to use in our generic code in CMyGenericClass
public static CMyOperations operator +(CMyOperations _IOne, CMyOperations _ITwo)
{
//use the implementation provided by the deriving class
return _IOne.AddMyOperations(_ITwo);
}
...
}
The implementation for these operators will be provided by our type 'T
', here CMyComplexNumber
:
class CMyComplexNumber : CMyOperations
{
public override CMyOperations AddMyOperations(CMyOperations _ITwo)
{
//... implement the addition here ...
}
};
In our generic class, we can force the type 'T
' to provide the desired operations, by using the 'where
' keyword:
class CMyGenericClass<T> where T : CMyOperations
{
...
}
The 'where
' keyword here means that the type 'T
' must be derived from CMyOperations
. Because CMyOperations
is abstract
, 'T
' must provide the implementation for the required operations. To sum it up by example:
/*
* class CMyOperations
*
* This class provides the operators that we would like our parameter type
* 'T' to provide.
* This is an abstract class - the actual implementation
* will be provided by the parameter type 'T'.
* The parameter type 'T' will derive from this class.
*/
abstract class CMyOperations
{
//an abstract method, which implements the operator+, and will be provided by type 'T'
public abstract CMyOperations AddMyOperations(CMyOperations _ITwo);
//the operator+, which we want to able to use in our generic code in CMyGenericClass
public static CMyOperations operator +(CMyOperations _IOne, CMyOperations _ITwo)
{
//use the implementation provided by the deriving class
return _IOne.AddMyOperations(_ITwo);
}
}
//Now, we can create types for our generic class to use:
class CMyComplexNumber : CMyOperations
{
private int iIValue = 0;
private int iJValue = 0;
public CMyComplexNumber(int _iIValue, int _iJValue)
{
iIValue = _iIValue;
iJValue = _iJValue;
}
//Implementing the operator+ from CMyOperations.
//This must return the containing type (CMyComplexNumber),
//so that the cast in CMyGenericClass<T> is successful.
public override CMyOperations AddMyOperations(CMyOperations _ITwo)
{
if(_ITwo is CMyComplexNumber)
{
CMyComplexNumber numberNew = new CMyComplexNumber(0, 0);
CMyComplexNumber number2 = _ITwo as CMyComplexNumber;
numberNew.iIValue = iIValue + number2.iIValue;
numberNew.iJValue = iJValue + number2.iJValue;
return numberNew;
}
else
{
System.Diagnostics.Debug.Assert(false,
"CMyComplexNumber.CMyOperations.AddMyOperations()
was invoked for an unsupported parameter type.");
}
return null;
}
public override string ToString()
{
return String.Format("{0}i {1}j", iIValue, iJValue);
}
}
We can now write generic code, which uses C# operators on the type parameter 'T
':
//the parameter type 'T' must inherit from CMyOperations,
//and so must provide the operators.
//This is an indirect way of specifying operator constraints on T
class CMyGenericClass<T> where T : CMyOperations
{
public T TestUsingOperatorsOnT(T _one, T _two)
{
//We need to cast from CMyOperations to T.
//This is safe enough, provided that T implements its
//AddMyOperations() method to return an object of its own type.
//Normally, this use of operator overloading would not be allowed by the compiler
T tTemp = (T)(_one + _two);
return tTemp;
}
};
Points of Interest
- There were a few other ways I tried to allow generic code to use operators - thought about using interfaces - however, we cannot have operators inside interfaces.
- Using the '
where
' keyword is an indirect way of specifying operator constraints on the type 'T
'.
History
- Submitted on 1st October, 2006