Add Functionality to Your Classes With Overloading

Give programmers options when working with your classes.

SharpShooter

LANGUAGES: C#

TECHNOLOGIES: Method Overloading | Constructor Overloading

 

Add Functionality to Your Classes With Overloading

Give programmers options when working with your classes.

 

By Mike Amundsen

 

One of many advantages of working with the .NET run time is the ability to take advantage of the full range of classic object-oriented programming (OOP) concepts. One of these concepts is overloading: It allows you to modify the number and type of parameters accepted by a method in a class. The ability to redefine the method signature makes adding new functionality and flexibility to your classes easy without cluttering the code with numerous optional parameters.

 

Another advantage of overloading is you can apply this technique to more than the methods of a class. You also can overload the class's constructor. This means you can allow programmers to pass parameters into your class at the exact moment they are creating an instance of the class itself. In some cases, you can use constructor parameters to reduce the lines of code necessary to use your objects.

 

In this article, you'll learn the basic concepts of method and constructor overloading, how to use them in your class designs, and a few general rules to apply when implementing constructor and method overloading. (Note that overloading differs from overriding. See the sidebar, "Overloading vs. Overriding.")

 

Harness the Power of Overloading

Constructor overloading is an elegant way to make your classes more usable. Often, simply adding optional constructors for your classes can make your classes easier to use and understand. You even can reduce the lines of code necessary to use your objects.

 

Constructor overloading lets you vary the type and number of parameters passed into the class when you first create an instance of it in code. You do this in C# by defining multiple constructor methods with different parameter signatures. For example, this code shows the default constructor and a second one that accepts a single parameter:

 

public class Constructors {

 

   public Constructors() {

   }

 

   public Constructors(int Value) {

   }

 

}

 

Notice that in C#, constructors are always public, they don't have a return value, and they always have the same name as the class (in Visual Basic .NET, constructors are always public subs named New).

 

Once you define alternate constructors that accept parameters, you can use the parameters to set values for class properties or other operations. For example, you might want to create a class that manages x and y coordinates for drawing objects (see Figure 1).

 

public class Point {

 

   public int x;

   public int y;

 

   public Point() {

   }

 

   public Point(int x, int y) {

       this.x = x;

       this.y = y;

   }

 

   public Point(Point p) {

       this.x=p.x;

       this.y=p.y;

   }

 

}

Figure 1. Adding multiple constructors can make your class easier to use.

 

An important thing to observe in Figure 1 is it actually defines three constructors: one that accepts no parameters, one that accepts two integers, and one that accepts a single object. The key thing to note here is if you want to create constructors that accept parameters and you still want to use the default constructor (with no parameters), you must add this no-parameter constructor to your code explicitly; if you don't, the class will always require that parameters be passed when creating an instance of the class. Of course, this is not a bad thing either.

 

The code in Figure 2 shows a simple ASP.NET page that uses the class shown in Figure 1; Figure 3 shows the resulting page in the browser.

 

<<%@ Page Language="C#" ClassName="PointSample" %>>

<>

<>

<>

    <

>

        <

>Point Constructor Sample

        <>

        <

>

            <

              runat="server" Text="Submit"/>>

        <

>

        <

>

            new Overloading.Point();<
>

            <>

        <

>

        <

>

            new Overloading.Point(10,20);<
>

            <>

        <

>

        <

>

            new Overloading.Point(p2);<
>

            <>

        <

>

    <

>

<>

<>

Figure 2. These classes, shown in Figure 1, have additional constructors that give programmers some options.

 


Figure 3. Having multiple constructors allows you to display resulting data in several ways.

 

Aside from convenience, using parameterized constructors also can simplify the code needed to create and use objects. For example, if you have an object named ProductInfo that uses two properties (PartNumber and UnitSize) to return useful data on a product in your catalog, you might write code like this to define a new product:

 

Overloading.ProductInfo p1 = new Overloading.ProductInfo();

p1.PartNumber="1";

p1.UnitSize="large";

p1.MinimumOrder=12;

p1.Price=2.50;

Label1.Text=p1.ToString();

p1 = null;

 

Although this looks pretty good, using parameters on the object constructor can reduce the lines of code, in this case by almost 50 percent:

 

Overloading.ProductInfo p2 =

 new Overloading.ProductInfo("3","large",24,3.75);

Label2.Text=p2.ToString();

p2 = null;

 

See Figure 4 for the ProductInfo class's constructor code.

 

public class ProductInfo {

 

   private string _PartNumber;

   private string _UnitSize;

   private int _MinimumOrder;

   private double _Price;

 

   public ProductInfo() {

   }

 

   public ProductInfo(string PartNumber,

                      string UnitSize,

                      int MinimumOrder,

                      double Price) {

       _PartNumber=PartNumber;

       _UnitSize=UnitSize;

       _MinimumOrder=MinimumOrder;

       _Price=Price;

   }

   ...

}

Figure 4. Adding a constructor that accepts several arguments can reduce the code necessary to use your class.

 

As you can see from the examples so far, you can make your code easier to work with and provide useful options for programmers working with your classes by using overloading with class constructors. Next, I'll show you how to achieve the same goals by using the overloading technique on the methods within your classes.

 

Add Flexibility With Method Overloading

Another way you can take advantage of overloading is by applying the technique to methods in your class. Overloading class methods allows you to create multiple copies of the same method with different parameter lists. This works in basically the same way as creating constructor overloads. For example, let's say you have a class named MethodClass, and it has a method named GetData that returns the uppercase version of any string you supply. However, you want to be able to call the GetData method in these different ways:

 

Set properties (Data, StartPos, and Size) on the object and have the method return an uppercase string of the proper size from the starting position indicated.

Pass a string with the method and have it return the full uppercase string.

Pass a string and a starting position and have the method return the rest of the string starting at the supplied location.

Pass a string, starting position, and string length and have the method return the substring of the desired length starting at the supplied location.

Mix and match the property and argument options in any way (setting the Data property and passing StartPos and Size, setting StartPos and passing the Data property, etc.).

 

The first step in creating a set of overloaded methods to meet these criteria is to devise all the possible argument options for the GetData method. If you remember to include a version that has no arguments at all, you'll find there are six methods:

 

public string GetData()

public string GetData(string Data)

public string GetData(string Data, int StartPos)

public string GetData(string Data, int StartPos, int Size)

public string GetData(int StartPos)     

public string GetData(int StartPos, int Size)

 

You might think there is one overload missing from this list - the one that passes only the Size argument. It's true that it is missing from the list, but it's missing for a good reason. We can't include GetData(int Size) because the compiler can't distinguish it from the GetData(int StartPos) overload. Because both overloads accept a single integer, the compiler throws an error when attempting to resolve references at compile time.

 

One way to implement those six overloads is to create a private method that does the real work of creating the return string and calling that private method from within the various overloaded public methods. Figure 5 shows how this might work for some of the method overloads.

 

public string GetData(string Data, int StartPos, int Size) {

   _Data=Data;

   _StartPos=StartPos;

   _Size=Size;

   return (FixString());

}

 

public string GetData(int StartPos) {

   _StartPos = StartPos;

   return (FixString());

}

     

public string GetData(int StartPos, int Size) {

   _StartPos = StartPos;

   _Size = Size;

   return (FixString());

}

 

private string FixString() {

   if (_Data!="") {

      if (_StartPos!=0) {

         if (_Size!=0) {

            return

             _Data.Substring(_StartPos,_Size).ToUpper();

         }

         return _Data.Substring(_StartPos).ToUpper();

      }

      return _Data.ToUpper();

   }

   else {

      return "";

   }

}

Figure 5. Using a private utility method to support all the overloaded methods makes implementing the various overload signatures easier.

 

The complete class called would contain the three public properties along with the code to implement all the listed method overloads. (See the Download box at the beginning of the article for details on how to get a complete copy of the class.) Figure 6 shows the complete ASP.NET page used to call the compiled MethodClass, and the resulting HTML page is shown in Figure 7.

 

<<%@ Page Language="C#" %>>

<>

 

<>

<>

    <

>

        <

>Method Overloading Sample<

>

        <


>

        <

>

            <

        runat="server" Text="Button"/>>

        <

>

        <

>

            GetData("testing")<
>

            <>

        <

>

        <

>

            GetData("testing",2)<
>

            <>

        <

>

        <

>

            GetData("testing",3,3)<
>

            <>

        <

>

        <

>

            GetData() [setting properties Data="testing",

               StartPos=4, Size=2]<
>

            <>

        <

>

        <

>

            GetData(2) [setting properties Data="testing")

            <
><>

        <

>

        <

>

            GetData(3,3) [Setting properties Data="testing")

            <
><>

        <

>

    <

>

<>

<>

Figure 6. A well-designed class offers a number of overloads for frequently used methods.

 


Figure 7. Multiple method overloads offer many ways to pass arguments for processing.

 

Guidelines for Successful Overloading

When you create classes that use overloaded constructors and methods, a few simple guidelines can help make easy-to-use and relatively error-free classes.

 

First, when using constructor overloads, be careful not to create constructors that need to create too many subobjects or handle too much processing. If you do, errors in the subobjects or errors that occur while attempting to complete long-running processes will complicate error handling in your code and also could slow the execution of the overall application.

 

The best way to use constructor overloads is to pass parameters that usually would be handled by using property settings. In other words, offer programmers the chance to skip property-setting code by passing parameters with the constructor instead.

 

Also, if you plan to add optional parameterized constructors, consider whether you should maintain the default version, without parameters, as well. If you wish to support a constructor with no parameters as well as one or more parameterized constructors, you'll need to add the version without parameters to the class file explicitly.

 

Finally, remember that overloading methods is based on the parameters passed into the method, not the return value or the argument names. For example, this code will not compile:

 

public class BadExample {

 

   public BadExample() {

   }

 

   public string Process(int Value) {

      return (Value+1).ToString();

   }

 

   public int Process(int NewValue) {

      return(NewValue+1);

   }

 

}

 

This code fails to compile because the type and number of parameters for the two Process methods are identical.

 

The files referenced in this article are available for download.

 

Mike Amundsen travels throughout the United States and Europe, speaking about and teaching a wide range of topics, including .NET, the Internet, team development, and leadership. He also has written more than a dozen books. His most popular titles are Sams Teach Yourself Database Programming with Visual Basic 6 in 21 Days (Sams) and Using Visual InterDev 6 (Que). E-mail Mike at mailto:[email protected].

 

Overloading vs. Overriding

The technique of overloading is different from overriding. When you overload a method name, you are adding another method to the list with a different set of arguments and possibly a different type of return value. You can place overloaded methods in either the base class or derived classes. For example, the code in Figure A shows a method in a base class and a corresponding overloaded method in a derived class.

 

namespace Overloading {

    using System;

 

    public class OvrTest {

 

        public string Method2() {

            return "Method2";

        }

    }

 

    public class OvrTest2 : OvrTest {

 

        public int Method2(int Size) {

            return Size;

        }

 

        public string Method2(int Size) {

           return Size.ToString();

         }

    }

}

Figure A. This code shows how you can place overloaded methods in the base class or in derived classes.

 

Notice that Method2 in the derived class does not have the same types of arguments or the same return type. You also can see that more than one overload of Method2 can exist in the same class. This is all perfectly legal (and quite handy) in C#.

 

When you override a method name, however, you are replacing an existing method in a base class with a new one in a derived class. You can't change the argument list or the return type when performing an override. You also can have only one override for each method. Finally, if you plan to allow others to perform an override of your method in corresponding derived classes, you usually need to include the virtual keyword when defining your method in the base class. (There are exceptions to this last rule, but that's material for a future article.)

 

For example, Figure B shows Method1 in the base class and the corresponding method1 override in the derived class.

 

namespace Overloading {

    using System;

 

    public class OvrTest {

 

        public virtual string Method1() {

            return "Method1";

        }

    }

 

    public class OvrTest2 : OvrTest {

 

        public override string Method1() {

            return "Method1a";

        }

    }

}

Figure B. In this figure, Method1 is in the base class, and the corresponding method1 override is in the derived class.

 

Notice that when overriding a method name, the method in the base class is decorated with the virtual keyword, and the method in the derived class is decorated with the override keyword. Also, notice that the argument list and the return types for both methods are identical; this is a requirement.

 

In summary, you can think of overloading as a way to create additional alternate signatures (arguments and return type) for an existing method, and you can think of overriding as a way to replace an existing method using the same signature.

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish