Practical .NET

Leveraging Interfaces and Extension Methods To Extend Your Classes

Extension methods provide a great way for extending a class functionality -- but it's interfaces that let you use those methods anywhere you want.

Extension methods let you add functionality to an existing class without having to modify the class's code, supporting Vogel's first law of software maintenance: "You don't screw with working code." And, unlike using inheritance to extend a class, with extension methods you don't have to replace the class you're extending with a new class: The Microsoft .NET Framework automatically attaches your extension method to whatever existing class you want the extension method to be part of.

But it's actually wrong for me to say that extension methods are attached to classes: Extension methods are really attached to interfaces, which makes extension methods an even more powerful (and flexible) tool for incorporating functionality into your application. I'm going to demonstrate what that means in this column.

If you're new to extension methods, see "Extension Method Basics" later in this article.

The Problem with Extension Methods
Imagine, for instance, that I have Customer class with a LogInfo method that writes out critical information about the Customer class (in real life, this information would go in a log file, but I'll just write to the console for this example). That method might look something like this:

Public Sub LogInfo()
  Console.WriteLine(Me.Name & ":" Me.KeyData)
End Sub

As useful as that method is, it exists in only one class. The method would be a lot more useful if it was an extension method so I could use it from other classes. Rewriting the method as an extension method that will still work with the Customer class gives me this code:

Public Module PHVExtensions
  <System.Runtime.CompilerServices.Extension()>
  Public Sub LogInfo(ByVal logSubject As Customer)
    Console.WriteLine(logSubject.Name & ": " logSubject.KeyData)
  End Sub
End Module

Unfortunately, this method will only work with classes that expose the Customer interface (that's the Customer class and any class that inherits from Customer). Inheritance makes all of those classes "look alike" to my extension method.

However, I want to do logging with many more classes than those that inherit from my Customer class. That makes it difficult to specify to which interface the LogInfo extension method should tie itself. I could tie my extension method to System.Object, except the method expects the class it's called from to have the properties Name and KeyData, which System.Object doesn't include. I suppose I could write some unreadable reflection code to extract the class's TypeName, but really, the programmer who creates the class should decide what counts as "KeyData" for the class.

Leveraging Interfaces
This is where interfaces come in to save the day. Like inheritance, interfaces are a way to make different classes "look alike." I can, for instance, define an interface called ILogInfo that specifies the properties that my LogInfo method requires:

Public Interface ILogInfo
  Property Name As String
  Property KeyData As String
End Interface

Any class, in addition to whatever objects it inherits from (or whatever other interfaces it already implements), can implement my ILogInfo interface. Here's a Visual Basic class that inherits from an object called CustomerBase and also implements both the IEnumerable and my ILogInfo interfaces (I've also included the properties required by ILogInfo):

Public Class Customer
  Inherits CustomerBase
  Implements IEnumerable
  Implements ILogInfo

Public ReadOnly Property Name() As String Implements ILogInfo.Name
  Return "Customer"
End Property

Public ReadOnly Property KeyData() As String Implements ILogInfo.KeyData
  Return Me.ID & ", " & Me.Status
End Property

Of course, when developers add my ILogInfo interface to a class, they'll be obliged to provide code for the methods and properties specified in the interface (Name and KeyData, in this example). That's actually a good thing: Each class that implements ILogInfo should provide what counts as KeyData for that class.

With this interface in place, I can rewrite my LogInfo method as an extension method that attaches itself to any class that implements ILogInfo:

Public Module PHVExtensions
  <System.Runtime.CompilerServices.Extension()>
  Public Sub LogInfo(ByVal logSubject As ILogInfo)
    Console.WriteLine(logSubject.Name & ": " logSubject.KeyData)
  End Sub
End Module

Going forward, for a class that I want to log information about, I now have to do two things: First, have that class implement ILogInfo; second, add to the Name and KeyData methods whatever code I think the class requires. As soon as I've done those two things I will find my LogInfo method appearing in the IntelliSense list for that class. It's almost magic. It's certainly cool.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • IDE Irony: Coding Errors Cause 'Critical' Vulnerability in Visual Studio

    In a larger-than-normal Patch Tuesday, Microsoft warned of a "critical" vulnerability in Visual Studio that should be fixed immediately if automatic patching isn't enabled, ironically caused by coding errors.

  • Building Blazor Applications

    A trio of Blazor experts will conduct a full-day workshop for devs to learn everything about the tech a a March developer conference in Las Vegas keynoted by Microsoft execs and featuring many Microsoft devs.

  • Gradient Boosting Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the gradient boosting regression technique, where the goal is to predict a single numeric value. Compared to existing library implementations of gradient boosting regression, a from-scratch implementation allows much easier customization and integration with other .NET systems.

  • Microsoft Execs to Tackle AI and Cloud in Dev Conference Keynotes

    AI unsurprisingly is all over keynotes that Microsoft execs will helm to kick off the Visual Studio Live! developer conference in Las Vegas, March 10-14, which the company described as "a must-attend event."

  • Copilot Agentic AI Dev Environment Opens Up to All

    Microsoft removed waitlist restrictions for some of its most advanced GenAI tech, Copilot Workspace, recently made available as a technical preview.

Subscribe on YouTube