Method Overloading

Listing 5.22 called DirectoryCountLines(), which counted the lines of *.cs files. However, if you want to count code in *.h/*.cpp files or in *.vb files, DirectoryCountLines() will not work. Instead, you need a method that takes the file extension but still keeps the existing method definition so that it handles *.cs files by default.

All methods within a class must have a unique signature, and C# defines uniqueness by variation in the method name, parameter data types, or number of parameters. This does not include method return data types; defining two methods that differ only in their return data types will cause a compile error. This is true even if the return type is two different tuples. Method overloading occurs when a class has two or more methods with the same name and the parameter count and/or data types vary between the overloaded methods.

note
A method is considered unique as long as there is variation in the method name, parameter data types, or number of parameters.

Method overloading is a type of operational polymorphism. Polymorphism occurs when the same logical operation takes on many (“poly”) forms (“morphs”) because the data varies. For example, calling WriteLine() and passing a format string along with some parameters is implemented differently than calling WriteLine() and specifying an integer. However, logically, to the caller, the method takes care of writing the data, and it is somewhat irrelevant how the internal implementation occurs. Listing 5.23 provides an example, and Output 5.9 shows the results.

Listing 5.23: Counting the Lines within *.cs Files Using Overloading
public static class LineCounter
{
    public static void Main(string[] args)
    {
        int totalLineCount;
 
        if(args.Length > 1)
        {
            totalLineCount = DirectoryCountLines(args[0], args[1]);
        }
        else if(args.Length > 0)
        {
            totalLineCount = DirectoryCountLines(args[0]);
        }
        else
        {
            totalLineCount = DirectoryCountLines();
        }
 
        Console.WriteLine(totalLineCount);
    }
 
    static int DirectoryCountLines()
    {
        return DirectoryCountLines(
            Directory.GetCurrentDirectory());
    }
 
    static int DirectoryCountLines(string directory)
    {
        return DirectoryCountLines(directory, "*.cs");
    }
 
    static int DirectoryCountLines(
        string directory, string extension)
    {
        int lineCount = 0;
        foreach(string file in
            Directory.GetFiles(directory, extension))
        {
            lineCount += CountLines(file);
        }
 
        foreach(string subdirectory in
            Directory.GetDirectories(directory))
        {
            lineCount += DirectoryCountLines(subdirectory);
        }
 
        return lineCount;
    }
 
    private static int CountLines(string file)
    {
        int lineCount = 0;
        string? line;
        // This can be improved with a using statement
        // which is not yet described.
        FileStream stream = new(file, FileMode.Open);
        StreamReader reader = new(stream);
        line = reader.ReadLine();
        while(line is not null)
        {
            if(line.Trim() != "")
            {
                lineCount++;
            }
            line = reader.ReadLine();
        }
 
        reader.Dispose();  // Automatically closes the stream
        return lineCount;
    }
}
Output 5.9
>LineCounter.exe .\ *.cs
28

The effect of method overloading is to provide optional ways to call the method. As demonstrated inside Main(), you can call the DirectoryCountLines() method with or without passing the directory to search and the file extension.

Notice that the parameterless implementation of DirectoryCountLines() was changed to call the single-parameter version, int DirectoryCountLines (string directory). This is a common pattern when implementing overloaded methods. The idea is that developers implement only the core logic in one method, and all the other overloaded methods will call that single method. If the core implementation changes, it needs to be modified in only one location rather than within each implementation. This pattern is especially prevalent when using method overloading to enable optional parameters that do not have values determined at compile time, so they cannot be specified using optional parameters.

note
Placing the core functionality into a single method that all other overloading methods invoke means that you can make changes in implementation in just the core method, which the other methods will automatically take advantage of.
{{ snackbarMessage }}
;