C# Overload Methods

by Sam Allen - Updated January 31, 2010
Overload

Although methods can branch on conditions in their bodies, overloaded methods can provide more information about their actual use to the compiler. You want to overload methods for improved code clarity and simpler logic. When designing complex programs, code changes and some branches will become unnecessary. Here we see how you can use overloaded methods to eliminate complexity and enhance performance, using the C# programming language.

Using overloaded methods

First, overloaded methods are completely separate from each other in the compiled program. A method receiving a string parameter is separate from one receiving no parameter or an integer. This programming technique results in method groups with a single identifier.

--- Example overloaded methods (C#) ---

class Program
{
    static void Main()
    {
        MethodA();
        MethodA("");
    }

    static void MethodA()
    {
    }

    static void MethodA(string a)
    {
    }
}

Example problem

Next we look at one situation where you can apply overloaded methods. Here we are not focusing on polymorphism and inheritance, but rather a simpler way of improving code readability and performance. Here's some code that is problematic.

--- Example code that could use overloaded methods (C#) ---
    This example could be improved by overloading.

using System;

class Program
{
    public static void Main()
    {
        ShowString(string.Empty);
        ShowString("Category");
    }

    static void ShowString(string value)
    {
        if (value == string.Empty)
        {
            Console.WriteLine("Popular");
        }
        else
        {
            Console.WriteLine(value);
        }
    }
}

Description. The code writes the word 'Popular' to the screen if the string argument is empty. Otherwise, it writes the category name. What's wrong with it? It is inefficient and confusing. We can simply avoid the string.Empty parameter entirely.

(See string.Empty Example and Issues.)

Improvements

The code above has an unnecessary branch, but this cannot be optimized out by the compiler in the IL code. The code calls into ShowString, but we know that the string is never empty at that call site. When optimizing C, the author learned that removing branches greatly increases performance. Processors use branch prediction, but this is not ideal. You want your code to go sequentially from beginning to end. We will use an overloaded method as one way to fix this problem.

--- Code that implements overloaded method parameter (C#) ---

using System;

class Program
{
    public static void Main()
    {
        ShowString();
        ShowString("Category");
    }

    static void ShowString()
    {
        // Send default argument to overload.
        ShowString("Popular");
    }

    static void ShowString(string value)
    {
        // We don't need an if check here, which makes
        // ... calling this method directly faster.
        Console.WriteLine(value);
    }
}

Description of program text. There is an overload with no parameters, and one with a string parameter. Overloaded methods always have different parameters. Look at what happens when ShowString is called the second time. There is no if statement. It goes directly from the call site to the Console.WriteLine part. The processor won't have to do any branch prediction, because there is no branch.

Intermediate language

Here we look into the IL (intermediate language) to see exactly what the overloaded methods become when first compiled. First, let's look inside the ShowString method that has the if statement.

(See Intermediate Language [IL].)

--- Compiled method that does not use overloading (IL) ---

.method private hidebysig static void  ShowString(string 'value') cil managed
{
  // Code size       31 (0x1f)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldsfld     string [mscorlib]System.String::Empty
  IL_0006:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_000b:  brfalse.s  IL_0018
  IL_000d:  ldstr      "Popular"
  IL_0012:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0017:  ret
  IL_0018:  ldarg.0
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001e:  ret
}

What we see. The if chain in ShowString uses an op_Equality. None of this is compiled out and it also can't be optimized away during JIT compilation. Therefore, ShowString will always have the branch. Next I show how the overloaded methods are called in the second example. The methods are completely separate. The compiler can easily tell the difference between overloads with different parameters.

--- Compiled methods that uses overloading (IL) ---

.method public hidebysig specialname rtspecialname
        instance void  .ctor() cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  call       void OverloadA::ShowString()
  IL_000b:  ldstr      "Category"
  IL_0010:  call       void OverloadA::ShowString(string)
  IL_0015:  ret
}

.method private hidebysig static void  ShowString() cil managed
{
  // Code size       11 (0xb)
  .maxstack  8
  IL_0000:  ldstr      "Popular"
  IL_0005:  call       void OverloadA::ShowString(string)
  IL_000a:  ret
}

Streamlined methods. Finally, the ShowString() method doesn't need any if-else chain at all. It simply does what it is told, without any checking at all. Your processor will pipeline these instructions more easily and the code will be more efficient.

Summary

In this walkthrough, we saw how you can refactor your code to use method overloading in the C# language when possible. The overloads are easily inferred by the compiler. You can streamline old code that contains unnecessary if-else chains, eliminating run-time inefficiencies by more clearly expressing the logic to the compiler. Overloaded methods, then, provide more static information during compile-time and typically results in better runtime performance.

(Do not copy this page.)

Dot Net Perls | Search
Methods | Func Type | Lambda Expression | Main Args Examples | Out Parameter Example 1 | Return Statement
C# | Obsolete Attribute | True and False | Gradient Tips | Catch Examples
© 2010 Sam Allen. All rights reserved.
Dot Net Perls | Sam Allen