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

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube