Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

While I learning C#, I found that, the C# supports operator overloading. I have problem with good example which:

  1. Make sense (ex. adding class named sheep and cow)
  2. Is not an example of concatenation of two strings

Examples from Base Class Library are welcome.

share|improve this question
9  
Define 'sense', please! Seriously, the bitter and impassioned debates over precisely this point show that there is huge disagreement about exactly this. Many authorities reject overloaded operators because they can be made to do utterly unexpected things. Others reply that method names can likewise be chosen to be completely unintuitive, but that is no reason for rejecting named code blocks! You are almost certainly not going to get any examples that are generally considered sensible. Examples that seem sensible to you - maybe. –  Kilian Foth Feb 23 '12 at 12:59
    
Completely agree with @KilianFoth. Ultimately the program that compiles, does make sense to compiler. But if overload == to do multiplication, it make sense to me but may not make sense to others! Is this question about legitimacy of what facility programming languages or are we talking about 'coding best practices'? –  Dipan Mehta Feb 24 '12 at 13:33

11 Answers 11

up vote 22 down vote accepted

The obvious examples of appropriate operator overloading are any classes which behave in the same way that numbers operate. So BigInt classes (as Jalayn suggests), complex numbers or matrix classes (as Superbest suggests) all have the same operations that ordinary numbers have so map really well onto mathematical operators, while time operations (as suggested by svick) map nicely onto a subset of those operations.

Slightly more abstractly, operators could be used when performing set like operations, so operator+ could be a union, operator- could be a complement etc. This does start to stretch the paradigm though, especially if you use the addition or multiply operator for an operation which isn't commutative, as you might expect them to be.

C# itself has an excellent example of non-numeric operator overloading. It uses += and -= to add and subtract delegates, i.e. register and de-register them. This works well because the += and -= operators work as you would expect them to, and this result in much more concise code.

For the purist, one of the problems with the string + operator is that it isn't commutative. "a"+"b" is not the same as "b"+"a". We understand this exception for strings because it is so common, but how can we tell if using operator+ on other types will be commutative or not? Most people will assume that it is, unless the object is string-like, but you never really know what people will assume.

As with strings, the foibles of matrices are pretty well known too. It is obvious that Matrix operator* (double, Matrix) is a scalar multiplication, whereas Matrix operator* (Matrix, Matrix) would be a matrix multiplication (i.e. a matrix of dot-product multiplications) for instance.

Similarly the use of operators with delegates is so obviously far removed from maths that you are unlikely to make those mistakes.

Incidentally, at the 2011 ACCU conference, Roger Orr & Steve Love presented a session on Some objects are more equal than others - a look at the many meanings of equality, value and identity. Their slides are downloadable, as is Richard Harris' Appendix about floating point equality. Summary: Be very careful with operator==, here be dragons!

Operator overloading is a very powerful semantic technique, but it is easy to over-use. Ideally you should only use it in situations when it is very clear from context what the effect of an overloaded operator is. In many ways a.union(b) is clearer than a+b, and a*b is much more obscure than a.cartesianProduct(b), especially since the result of a cartesian product would be a SetLike<Tuple<T,T>> rather than a SetLike<T>.

The real problems with operator overloading come when a programmer assumes a class will behave in one way, but it actually behaves in another. This sort of semantic clash is what I'm suggesting it is important to try to avoid.

share|improve this answer
1  
You say that operators on matrices map really well, but matrix multiplication isn't commutative either. Also operators on delegates are even stronger. You can do d1 + d2 for any two delegates of the same type. –  svick Feb 23 '12 at 18:29
1  
@Mark: The "dot product" is only defined on vectors; multiplying two matrices is called simply "matrix multiplication." The distinction is more than just semantic: the dot product returns a scalar, while matrix-multiplication returns a matrix (and is, by the way, non-commutative). –  BlueRaja - Danny Pflughoeft Feb 23 '12 at 20:21

I'm surprised nobody mentioned one of the more interesting cases in BCL: DateTime and TimeSpan. You can:

  • add or subtract two TimeSpans to get another TimeSpan
  • use unary minus on a TimeSpan to get a negated TimeSpan
  • subtract two DateTimes to get a TimeSpan
  • add or subtract TimeSpan from a DateTime to get another DateTime

Another set of operators that could make sense on a lot of types are <, >, <=, >=. In the BCL, for example Version implements them.

share|improve this answer

The first example that comes to my mind is the implementation of BigInteger, which allows you to work with large signed integers. Check out the MSDN link to see how many operators have been overloaded (that is, there is a big list, and I did not check if all operators have been overloaded, but it certainly seems so)

Also, since I also do Java and Java does not allow overloading operators, it's incredibly sweeter to write

BigInteger bi = new BigInteger(0);
bi += 10;

Than, in Java:

BigDecimal bd = new BigDecimal(0);
bd = bd.add(new BigDecimal(10));
share|improve this answer

I'm glad I saw this because I've been fooling around with Irony and it has a GREAT use of operator overloading. Here is a sample of what it can do.

So Irony is a ".NET Language Implementation Kit" and is a parser generator (generating an LALR parser). Instead of having to learn a new syntax/language like parser generators such as yacc/lex you write the grammar in C# with the operator overload. Here is a simple BNF grammar

// BNF 
Expr := Term | BinExpr
Term := number | ParExpr
ParExpr := "(" + Expr + ")"
BinExpr := number + BinOp + number
BinOp := "+" | "-" | "*" | "/"
number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

So it is a simple little grammar (please excuse it if there are inconsistencies as I'm just learning BNF and constructing grammars). Now lets look at the C#:

  var Expr = new NonTerminal("Expr");
  var Term = new NonTerminal("Term");
  var BinExpr = new NonTerminal("BinExpr");
  var ParExpr = new NonTerminal("ParExpr");
  var BinOp = new NonTerminal("BinOp");
  var Statement = new NonTerminal("Statement");
  var ProgramLine = new NonTerminal("ProgramLine");
  var Program = new NonTerminal("Program", typeof(StatementListNode));
  // BNF Rules - Overloading
  Expr.Rule = Term | BinExpr;
  Term.Rule = number | ParExpr;
  ParExpr.Rule = "(" + Expr + ")";
  BinExpr.Rule = Expr + BinOp + Expr;
  BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**";

As you can see, with the operator overloading, writing the grammar in C# is almost exactly writing the grammar in BNF. To me, that not only makes sense but is a great use of operator overloading.

share|improve this answer

The key example is operator==/operator!=.

If you want to easily compare two objects with by data values instead of by reference, you'll want to overload .Equals (and.GetHashCode!), and might want to do the != and == operators as well for consistency.

I've never seen any wild overloads of other operators in C# though (I imagine there are edge cases where it might be useful though).

share|improve this answer

This example from MSDN shows how to implement complex numbers and have them use the normal + operator.

Another example shows how to do it for matrix addition, and also explains how not to use it to add a car to a garage (read the link).

share|improve this answer

Good use of overload can be rare, but it does happen.

overloading operator== and operator!= show two schools of thought: those for saying it makes things easier, and those against saying it prevents comparing addresses (i.e. am I pointing to the exact same place in memory, not just a copy of the same object).

I find cast operator overloads to be handy in specific situations. For example, I had to serialize/deserialize in XML a boolean represented as 0 or 1. The right (implicit or explicit, I forget) cast operator from boolean to int and back did the trick.

share|improve this answer
3  
It doesn't prevent comparing addresses: You can still use object.ReferenceEquals(). –  dan04 Feb 23 '12 at 20:17
    
@dan04 Very very good to know! –  MPelletier Feb 23 '12 at 20:55
    
Another way to compare addresses is to force the use of object's == by casting: (object)foo == (object)bar always compares references. But I would prefer ReferenceEquals(), as @dan04 mentions because it's clearer what it does. –  svick Feb 24 '12 at 12:33

They're not in the category of things that people typically think of when they thing of operator overloading, but I think one of the most important operators to be able to overload is the conversion operator.

Conversion operators are especially useful for value types that can "de-sugar" to a numeric type, or can act like a numeric type in some contexts. For example, you might define a special Id type that represents a certain identifier, and you could provide an implicit conversion to int so that you can pass an Id to a method that takes an int, but an explict conversion from int to Id so no one can pass an int into a method that takes an Id without casting it first.

As an example outside of C#, the Python language includes many special behaviors that are implemented as overloadable operators. These include the in operator for membership testing, the () operator for calling an object as if it's a function, and the len operator for determining the length or size of an object.

And then you have languages like Haskell, Scala, and many other functional languages, where names like + are just ordinary functions, and not operators at all (and there is language support for using functions in infix position).

share|improve this answer

The Point Struct in the System.Drawing namespace uses overloading to compare two different locations using operator overloading.

 Point locationA = new Point( 50, 50 );
 Point locationB = new Point( 50, 50 );

 if ( locationA == locationB )
    Console.WriteLine( "Their locations are the same" );
 else
    Console.WriteLine( "Their locations  are different" );

As you can see, it is much easier to compare the X and Y co-ordinates of two locations using the overload.

share|improve this answer

If you are familiar with the mathematical vector you might see a use in overloading the + operator. You could add a vector a=[1,3] with b=[2,-1] and get c=[3,2].

Overloading the equals (==) can also be a useful (even though it's probably better to implement a equals() method). To continue the vector examples:

v1=[1,3]
v2=[1,3]
v1==v2 // True
share|improve this answer

Imagine a piece of code for drawing on a form

{
  Point p = textBox1.Location;
  Size dp = textBox1.Size;

  // Here the + operator has been overloaded by the CLR
  p += dp;  // Now p points to the lower right corner of the textbox.
  ..
}

Another common example is when a structure is used to hold position information in the form of a vector.

public struct Pos
{
    public double x, y, z;
    public double Distance { get { return Math.Sqrt(x * x + y * y + z * z); } }
    public static Pos operator +(Pos A, Pos B)
    {
        return new Pos() { x = A.x + B.x, y = A.y + B.y, z = A.z + B.z };
    }
    public static Pos operator -(Pos A, Pos B)
    {
        return new Pos() { x = A.x - B.x, y = A.y - B.y, z = A.z - B.z };
    }
}

only to be used later as

{
    Pos A = new Pos() { x = 4, y = -1, z = 0.5 };
    Pos B = new Pos() { x = 8, y = 2, z = 1.5 };

    double x = (B - A).Distance;
}
share|improve this answer
3  
You add vectors, not positions :\ This is a good example of when operator+ should not be overloaded (you can implement a point in terms of a vector, but you should not be able to add two points) –  BlueRaja - Danny Pflughoeft Feb 23 '12 at 20:28
    
@BlueRaja-DannyPflughoeft: Adding positions to yield another position doesn't make sense, but subtracting them (to yield a vector) does, as does averaging them. One could compute the average of p1, p2, p3, and p4 via p1+((p2-p1)+(p3-p1)+(p4-p1))/4, but that seems somewhat awkward. –  supercat Feb 24 at 23:56
    
In affine geometry you can do algebra with points and lines, like addition, scaling etc. The implementation though requires homogeneous coordinates, which are typically used in 3D graphics anyway. The addition of two points actually results in their average. –  ja72 Feb 25 at 13:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.