Extension Methods
Learn how to add new methods to existing types without modifying their source code using extension methods.
We'll cover the following...
We often need to extend the functionality of a class or struct. We can use several approaches depending on the type and our access to the source code.
Update source code
We could go to the source code of the type and make whatever changes we need. Consider the following Printer class. Its purpose is to print the Message property in a variety of ways. If we need additional functionality, we can simply add another method or edit existing ones.
Line 1: We declare a file-scoped namespace to organize our code without extra indentation.
Line 3: We define the
Printerclass.Lines 5–10: We define a property and a constructor to initialize the printer with a message.
Lines 13–16: We add a new method
PrintWithDashesdirectly to the class.
This approach is only useful if we control the source code of the type we want to modify. This isn’t possible in most cases because the code is likely managed by a different person, team, or company.
Now, let’s look at how we would use this modified class in our application.
Line 2: We import the
MainProgramnamespace so we can use thePrinterclass.Line 5: We create a new instance of
Printerusing target-typednew.Line 7: We call the method we added to the source code.
Extend with inheritance
Let’s assume we cannot change the Printer class source code, so it remains in its original state. Another approach we could take is to inherit from the type we want to extend. For our Printer class, we can create a new class that inherits from it to add the necessary methods and properties.
Line 6: We define
StarPrinterand inherit from thePrinterclass.Line 8: We chain the constructor to the base class constructor using
base(message).Line 14: We add the new
PrintWithStarsmethod to this derived class.
Now, we can use our derived StarPrinter to access both the old and new functionality.
Line 4: We instantiate
StarPrinterinstead ofPrinter.Line 7: We call the original method inherited from
Printer.Line 10: We call the new method defined in
StarPrinter.
This approach fails if the type is sealed or if we are working with value types (structs), as they do not support inheritance.
Extension methods
Extension methods provide a solution when we cannot change the source code or inherit from the target type.
Consider the following example. We want to add a method to the System.String class that allows us to reverse a string. We don’t have access to the source code of the System.String class, so we can’t add a new method there. We can create an extension method, however.
Line 4: We define a
staticclass. Extension methods must reside inside a static class.Line 8: We define a
staticmethod with thethismodifier on the first parameter. The type of the parameter followingthisspecifies the type the method extends (in this case,string).Lines 10–12: We convert the string to an array, reverse it, and return the new string.
Extension methods are static methods that have the this keyword before the first parameter. The type of the parameter following this specifies the type the method extends.
Note: Extension methods can only be hosted inside static classes, and to be able to use extension methods, we must import the namespace in which the static class resides.
Now we can use this method on any string object as if it were a native member of the class.
Line 2: We import the
Extensionsnamespace to make the extension methods available.Lines 9–10: We call
.Reverse()directly on the string variablesnameandcompany.
Extending sealed types
Extension methods are also useful when the type we want to extend is marked with the sealed keyword. We can’t inherit from a sealed type, but we can extend it with extension methods.
First, let’s look at the Person class, which is sealed.
Line 4: We mark the class as
sealed, preventing inheritance.Lines 6–10: We define standard properties for the person.
Next, we define an extension method to calculate the monthly salary.
Line 2: We import
MainProgramso we can reference thePersonclass.Line 8: We define the extension method
GetMonthlySalaryfor thePersontype usingthis Person.Line 10: We perform the calculation using the public
AnnualSalaryproperty.
Finally, let’s see how this works in the main program.
Lines 1–2: We import the necessary namespaces.
Line 4: We instantiate a
Personobject using target-typednew.Line 15: We call
GetMonthlySalary()on thepersoninstance, even though the method is not defined insidePerson.cs.