Overloading Operators in C#

In this seventh part of a ten-part series on C#, you will learn how and why to overload operators, and more. This article is excerpted from chapter four of C# 3.0 in a Nutshell, Third Edition, A Desktop Quick Reference, written by Joseph Albahari and Ben Albahari (O'Reilly; ISBN: 0596527578). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 4
October 30, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Scenarios for Nullable Types

One of the most common scenarios for nullable types is to represent unknown values. This frequently occurs in database programming, where a class is mapped to a table with nullable columns. If these columns are strings (e.g., an Email-Address column on a Customer table), there is not a problem as string is a reference type in the CLR, which can be null. However, most other SQL column types map to CLR struct types, making nullable types very useful when mapping SQL to the CLR. For example:

  // maps to a Customer table in a database
  public class Customer
  {
    ...
    
public decimal? AccountBalance;
  }

A nullable type can also be used to represent the backing field of an ambient property. An ambient property, if null, returns the value of its parent. For example:

  public class Row
  {
   
...
   
Grid parent;
   
Color? backColor;

    public Color BackColor
   
{
     
get { return backColor ?? parent.BackColor; }
     
set { backColor = backColor == parent.BackColor ? null : value; }
   
}
  }

Alternatives to Nullable Types

Before nullable types were part of the C# language (i.e., before C# 2.0), there were many strategies to deal with nullable value types, examples of which still appear in the .NET Framework for historical reasons. One of these strategies is to designate a particular nonnull value as the “null value”; an example is in the string and array classes. String.IndexOf returns the magic value of -1 when the character is not found:

  int i = "Pink".IndexOf ('b');
  onsole.WriteLine(s);         // outputs -1

However,Array.IndexOfreturns-1only if the index is 0-bounded. The more general formula is thatIndexOfreturns 1 less than the minimum bound of the array. In the next example,IndexOfreturns0when an element is not found:

  // Create an array whose lower bound is 1 instead of 0:

  Array a = Array.CreateInstance (typeof(string),

              new int[] {2}, new int[] {1});
  a.SetValue("a", 1);
  a.SetValue("b", 2);
  Console.WriteLine(Array.IndexOf(a, "c")); // outputs 0

Nominating a “magic value” is problematic for several reasons:

  1. It means that each value type has a different representation of null. In contrast, nullable types provide one common pattern that works for all value types.
  2. There may be no reasonable designated value. In the previous example, –1 could not always be used. The same is true for our earlier examples representing an unknown account balance and an unknown temperature.
  3. Forgetting to test for the magic value results in an incorrect value that may go unnoticed until later in execution—when it pulls an unintended magic trick. Forgetting to testHasValueon a null value, however, throws anInvalidOperationExceptionon the spot.

  4. The ability for a value to be null is not captured in the type. Types communicate the intention of a program, allow the compiler to check for correctness, and enable a consistent set of rules enforced by the compiler.

Operator Overloading

Overview

Operators can be overloaded to provide more natural syntax for custom types. Operator overloading is most appropriately used for implementing custom structs that represent fairly primitive data types. For example, a custom numeric type is an excellent candidate for operator overloading.

Table 4-1 lists the overloadable symbolic operators.

Table 4-1. Overloadable symbolic operators

+ (unary)

- (unary)

!

~

++

--

+

-

*

/

%

&

|

^

<<

>>

==

!=

>

<

>=

<=

 

 

 

The following operators are also overloadable:

  1. Implicit and explicit conversions (with theimplicitandexplicitkeywords)

  2. The literalstrueandfalse

The following operators are indirectly overloaded:

  1. The compound assignment operators (e.g.,+=,/=) are implicitly overridden by overriding the noncompound operators (e.g.,+,=).

  2. The conditional operators&&and||are implicitly overridden by overriding the bitwise operators&and|.

Operator Functions

An operator is overloaded by declaring an operator function. An operator function has the following rules:

  1. The name of the function is specified with theoperatorkeyword followed by an operator symbol.
  2. The operator function must be markedstatic.

  3. The parameters of the operator function represent the operands.

  4. The return type of an operator function represents the result of an expression.

  5. At least one of the operands must be the type in which the operator function is declared.

In the following example, we define a struct calledNoterepresenting a musical note, and then overload the+operator:

  public struct Note
  {
    int value;
    public Note (int semitonesFromA) { value = semitonesFromA; }

    public static Note operator + (Note x, int semitones)
   
{
       return new Note (x.value + semitones);
    }
  }

This overload allows us to add anintto aNote:

  Note B = new Note(2);
  Note CSharp = B + 2;

Overloading an assignment operator automatically supports the corresponding compound assignment operator. In our example, since we overrode+, we can use+=too:

  CSharp += 2;

Overloading Equality and Comparison Operators

Equality and comparison operators are sometimes overridden when writing structs, and in rare cases when writing classes. Special rules and obligations come with overloading the equality and comparison operators, which we explain in Chapter 6. A summary of these rules is as follows:

Pairing

The C# compiler enforces operators that are logical pairs to both be defined. These operators are (==!=), (<>), and (<= >=).

Equalsand GetHashCode

In most cases, if you overload (==) and (!=), you need to override theEquals andGetHashCode methods defined onobjectin order to get meaningful behavior. The C# compiler will give a warning if you do not do this. (See the section “Equality comparison ” in Chapter 6 for more details.)

IComparableand IComparable<T>

If you overload (<>) and (<= >=), you should implementIComparableand IComparable<T>.

Custom Implicit and Explicit Conversions

Implicit and explicit conversions are overloadable operators. These conversions are typically overloaded to make converting between strongly related types (such as numeric types) concise and natural.

To convert between weakly related types, the following strategies are more suitable:

  1. Write a constructor that has a parameter of the type to convert from.
  2. WriteToXXX andFromXXX methods to convert between types.

As explained in the discussion on types, the rationale behind implicit conversions is that they are guaranteed to succeed and do not lose information during the conversion. Conversely, an explicit conversion should be required either when runtime circumstances will determine whether the conversion will succeed or if information may be lost during the conversion.

In this example, we define conversions between our musicalNotetype and a double (which represents the frequency in hertz of that note):

  ...
  // Convert to hertz
  public static implicit operator double(Note x)
  {

    return 440 * Math.Pow (2,(double) x.value / 12 );
  }
 
// Convert from hertz (only accurate to nearest semitone)
  public static explicit operator Note(double x)
  {
    return new Note ((int) (0.5 + 12 * (Math.Log(x/440) / Math.Log(2)) ));
  }
  ...

  Note n =(Note)554.37;  // explicit conversion
  double x = n;          // implicit conversion

Following our own guidelines, this example might be better implemented with aToFrequencymethod (and a staticFromFrequencymethod) instead of implicit and explicit operators.

Overloading true and false

The true and false operators are used in the extremely rare case of operators defining types with three-state logic to enable these types to work seamlessly with conditional statements and operators—namely, the if, do, while,for, and?:. TheSystem.Data.SqlTypes.SqlBooleanstruct provides this functionality. For example:

 

  class Test
 
{
    static void Main()
    {
     
SqlBoolean a = SqlBoolean.Null;
      if (a)
        Console.WriteLine("True");
      else if (! a)
        Console.WriteLine("False");
      else
        Console.WriteLine("Null");
    }
  }

  OUTPUT:
  Null

The following code is a reimplementation of the parts ofSqlBooleannecessary to demonstrate thetrueandfalseoperators:

  public struct SqlBoolean
  {
    public static bool operator true (SqlBoolean x)
    {
     
return x.m_value == True.m_value;
    }

    public static bool operator false (SqlBoolean x)
    {
      return x.m_value == False.m_value;
    }

    public static SqlBoolean operator !(SqlBoolean x)
    {
      if (x.m_value == Null.m_value) return Null;
      if (x.m_value == False.m_value) return True;
      return False;
   
}

    public static readonly SqlBoolean Null = new SqlBoolean(0);
    public static readonly SqlBoolean False = new SqlBoolean(1);
    public static readonly SqlBoolean True = new SqlBoolean(2);

    private SqlBoolean (byte value) {m_value = value;}
    private byte m_value;
  }

Please check back next week for the continuation of this article.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 3 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials