Practical .NET

Make Objects Look Alike... and Tell Them Apart

Here's everything you need to know to simplify your code about how to make classes look alike and then, when you need to, tell them apart.

When designing classes you shouldn't hesitate to design specific objects for specific tasks. This can complicate code that has to deal with multiple types of objects. To simplify that code you have to make different objects look alike... and then be able to tell them apart. You've got a bunch of options in both languages to do that -- and one more in C#.

For instance, let's say you have three types of classes -- Customer, PremiumCustomer, and DeadbeatCustomer -- but you have a routine that checks the CreditRating on any of them. You could declare the parameter that the method accepts as type Object, for instance, and have it work with anything:

Public Function CheckCreditRating (cust as Object) As Boolean

End Function

In C#:

public bool CheckCreditRating (Object cust)
{

}

You can do this because everything in .NET inherits from Object -- you can declare a variable as any type in a class' inheritance chain and the variable will work the object. If you have a PremiumCustomer and a DeadBeatCustomer that both inherit from Customer, then you can declare your variable as Customer:

Public Function CheckCreditRating (cust as Customer) As Boolean

End Function

public bool CheckCreditRating (Customer cust)
{

}

Effectively then, inheritance makes different objects look alike: All the .NET objects can be accessed with a variable declared as Object because everything inherits from Object. PremiumCustomer and DeadBeatCustomer objects can be accessed with a variable declared as Customer if both classes inherit from Customer.

Interfaces provide another, more flexible way for different objects to look alike. While a class can only inherit from one other class, it can implement as many interfaces as you want. Here's the Visual Basic way of declaring an interface:

Public Interface ICustomer

End Interface

Here's the C# way:

public interface ICustomer
{

}

Now you can implement the interface in any class you want:

Public Class Customer
	Implements ICustomer

End Class

public class Customer: ICustomer
{

}

You don't have to have your interface names begin with the letter I, but it's a .NET tradition to do so.

If Customer, PremiumCustomer and DeadBeatCustomer all implement the ICustomer interface, then you can declare a variable as ICustomer and use it with all three classes. These methods can accept any class that implements the ICustomer interface:

Public Function CheckCreditRating (cust as ICustomer) As Boolean

End Function

public bool CheckCreditRating (ICustomer cust)
{

}

Of course, nothing is free. If you make objects look alike through inheritance, you also pick up the code built into any class you inherit from. Using interfaces obliges you to write all the code for the interface's methods and properties. If you aren't sharing functionality, interfaces are your best choice -- especially if you want classes that would otherwise have nothing to do with each other to look alike. If I wanted to be able to treat Customer and Vendor objects alike when it comes to checking credit ratings, I wouldn't try to have Customers inherit from Vendors or both of them inherit from some common object: I'd have them all implement an ICreditRating interface.

Telling Them Apart
The trick is that you can only access the methods and properties that are available through the interface you've declared your variable with. For instance, if you declare your variable as Object, then your variable will only give you access to the members of the Object class' interface (GetType, ToString, etc.). If you declare a variable as Customer, then you'll only be able to access the members defined in the Customer class. If you define a method in the PremiumCustomer class that isn't part of the base Customer class, you won't be able to access that method with a variable defined as Customer.

So you'll need to do a cast. With objects, when you cast a variable what you're really doing is picking which interface on the object you'll be accessing. This Visual Basic code allows you to access the PremiumCustomer interface on the object passed into the method:

Public Function CheckCreditRating (cust as Customer) As Boolean
Dim pm As PremiumCustomer

pm = CType(cust, PremiumCustomer)

End Function

Even as a long time Visual Basic coder, I have to admit that I think the C# syntax for casting is more expressive than the Visual Basic syntax:

public bool CheckCreditRating (Customer cust)
{
 PremiumCustomer pm;

 pm = (PremiumCustomer) cust;
}

Of course, if the object passed in doesn't have the PremiumCustomer interface (in this object model that would mean that the object passed in wasn't actually a PremiumCustomer) this code will blow up. So sensible programmers will check to make sure the cast will work before trying it. In Visual Basic you use the TypeOf keyword like this:

If TypeOf cust Is PremiumCustomer Then
            pm = CType(cust, Customer)
End If

In C#, the code uses the is keyword like this:

if (cust is PremiumCustomer)
{
   pm = (PremiumCustomer) cust;
}

That's a lot of boring code to write (and have to do it again and again). Both languages provide a way to check if the interface is available and extract it (if the interface isn't available, the result is Nothing or null). In Visual Basic you use the DirectCast function to set your variable and check to see if it was set to Nothing, like this:

pm = DirectCast(cust, PremiumCustomer)
If pm IsNot Nothing Then
            ... do something with pm as a PremimumCustomer

In C#, you use the as keyword, like this:

pm = cust as PremiumCustomer;
if (pm != null)
{
  ... do something with pm as a PremimumCustomer

But, in C# 4, you have another option: You can declare your variable as dynamic and let the compiler figure out if the methods are available at runtime:

public void CheckCreditRating(dynamic cust)
{
   if (cust is PremiumCustomer)
   {
     ... use cust as a PremiumCustomer

You'll give up IntelliSense support at design time (and a couple of milliseconds of performance at runtime), but the code is a little bit cleaner.

There you go: Everything you need to know about making classes look alike and then tell them apart.

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

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube