Click here to Skip to main content
Click here to Skip to main content

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

, 1 Oct 2006
Rate this:
Please Sign up or sign in to vote.
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

  1. 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.
  2. Using the 'where' keyword is an indirect way of specifying operator constraints on the type 'T'.

History

  • Submitted on 1st October, 2006

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

theory2006
Web Developer
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralExcellent Article PinmemberAWERT123200321-Feb-08 10:43 
GeneralUnpractical PinmemberThe .NET Junkie5-Oct-06 5:32 
GeneralNice Article PinmemberSimon McEnlly3-Oct-06 17:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 1 Oct 2006
Article Copyright 2006 by theory2006
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid