Understanding Interfaces and Their Usefulness
By Tim Stall
Introduction
Many large Web applications in existence make use of User Controls, which are a unit of user interface reuse in ASP.NET.
Common UI pieces - such as menus, page headers or footers, and so on - can be easily encapsulated into a User Control for reuse
on numerous other pages. In their simplest form, User Controls are statically added to a Web page, dragged on from the
Solution Explorer in Visual Studio .NET. However, User Controls can also be dynamically added to a page, a technique
commonly used for providing customized user interfaces.
When multiple developers start building a large scale ASP.NET application that utilizes dynamically-added User Controls, the
site's design often suffers from two common problems:
Quality control, and
Flexibility
One reason quality control suffers is because it is natural for developers to give the functionality of their components
different method names. For example three developers creating similar components may opt to name related functions
with different names. Second, flexibility will suffer unless there is a convenient way to reference dynamically called
components. For example, imagine a particular ASP.NET page loads a set of User Controls specified through querystring
values. Once these controls have been loaded, they each might have differently named methods or properties that need
to be called/set. With a gaggle of different User Controls that could be loaded, and each with different method and
property names, the page's source code will quickly become unreadable.
It is possible, though, to solve these quality control and flexibility issues through the use of .NET interfaces.
This article explores how to use interfaces to simplify development of a site with dynamically loaded User Controls.
(This article does not provide an in-depth look at User Controls or at techniques for dynamically loading Web
controls. For more information on these topics be sure to read An
Extensive Examination of User Controls and Dynamic Controls in ASP.NET.)
Working with Dynamic User Controls - The Non-Interface Solution
In order to fully understand the benefits of using interfaces when building a Web application that uses dynamically-loaded
User Controls, let's first look at an example of not using interfaces. Assume that we have a page that loads
one of three User Controls based upon the querystring, and that later on in the code we need to read the value
of a property. If each control has a different name for the property that we want to call, we'll need to determine
what control was loaded and then read its appropriate property. There are a number of ways this could be accomplished, but
they all boil down to a series of conditional statements, like:
// Determine what User Control was loaded based on the querystring value
string strSection = Request.QueryString["Section"];
string str = "";
// Use a switch statement to reference the correct User Control
switch (strSection)
{
case "A":
str = ((NoneA)this.PlaceHolder1.Controls[0]).ControlData;
break;
case "B":
str = ((NoneB)this.PlaceHolder1.Controls[0]).Values;
break;
case "C":
str = ((NoneC)this.PlaceHolder1.Controls[0]).SelectedItems;
break;
}; //end of switch-case
As the above example shows, one of three User Controls - NoneA, NoneB, or NoneC - has
its appropriate property read, based on the querystring value. By inspecting the code you'll likely find
a number of limitations that could be remedied:
Each control has a different property name for the same functionality (ControlData vs. Values
vs. SelectedItems)
Reading the appropriate property requires a switch statement that has one section for each different type
of control to be loaded. If there were many types of User Controls that might be loaded, the switch statement
could quickly become tediously long and unreadable.
The possible User Controls are hard-coded into the switch statement, so we would need to modify the code
if a new type of User Control could be added.
These limitations are inherent because we are not using interfaces. In the next section we'll see how interfaces give us
the best of both worlds by easily solving the aforementioned problems and providing quicker User Control development time.
An Overview on Interfaces
Interfaces are a common concept in object-oriented programming. From the
Java documentation:
In English, an interface is a device or a system that unrelated entities use to interact. According to this definition, a
remote control is an interface between you and a television set, the English language is an interface between two people,
and the protocol of behavior enforced in the military is the interface between people of different ranks. Within
[an object-oriented] ... programming language, an interface ... is a device that unrelated objects use to interact with
each other. An interface is probably most analogous to a protocol (an agreed on behavior). ...
You use an interface to define a protocol of behavior that can be implemented by any class anywhere in the class hierarchy. Interfaces are useful for the following:
Capturing similarities among unrelated classes without artificially forcing a class relationship.
Declaring methods that one or more classes are expected to implement.
Revealing an object's programming interface without revealing its class.
.NET provides an interface type that you can use to formally spell out a list of member signatures. Any class that implements
an interface, must implement the list of member signatures defined by the interface.
This assists quality control by enforcing the class members to use the same names.
It also assists flexibility by providing interface polymorphism, which is similar to polymorphism with base classes.
Writing an interface is quick and easy, as the code below shows:
using System;
namespace Demo.WithInterfaces
{
/// <summary>
/// Summary description for ISection.
/// </summary>
public interface ISection
{
string GetData { get; }
void ClearFields();
} //end of interface
} //end of namespace
This code creates an interface named ISection.
Notice how just like regular class files, an interface starts with the namespaces it uses, and can itself be wrapped in a
namespace. However it differs from a class by using the interface keyword instead of the class
keyword, and by having a body that is only a list of signatures without any actual implementation. This particular
interface declares a read-only property named GetData that returns a string, and a method ClearFields().
Although an interface is similar to a base class, it is also very different. They are similar in that:
Both provide structure to another class that uses them
Both can be used for polymorphism
Interfaces are different than base classes in that:
A class can inherit only one base class, but it can implement many interfaces
A base class can contain implementation, an interface cannot
Interfaces can only contain Methods, Properties, Indexers, and Events. They cannot contain fields or constants
(which are used for implementation)
Also, as Microsoft's Krzysztof Cwalina points out in his blog entry
API Design Myth: Interface as Contract,
interfaces don't specify a contract. Rather, they simply specify the syntax. If you want to guarantee semantics, you need
to use a base class.
Dynamically-Loaded User Controls - The Interface Solution
Once you have created an interface, the next step is to have your User Controls implement the interface. To specify that
a class must implement a particular interface use the following syntax:
// C#
public class ClassName : Interface
-- or, the class is derived from a base class --
public class ClassName : BaseClass, Interface
To have your User Control implement the ISection interface, go to the User Control's code portion and modify the
class declaration so that it implements the interface:
public class WithA : System.Web.UI.UserControl, ISection
If you are using Visual Studio .NET, once you enter this code
you'll see a tooltip pop-up that prompt you to press TAB in order to automatically create the stub of the interface, as shown
in the screenshot below.
Press TAB, and you'll see the following code created:
#region ISection Members
public string GetData
{
get
{
// TODO: Add WithA.GetData getter implementation
return null;
}
}
public void ClearFields()
{
// TODO: Add WithA.ClearFields implementation
}
#endregion
If you try changing the names of this property or method, or remove the property or method, you'll get a compile-time error
since not all of the interfaces members will be implemented in the class. By having all of our User Controls that can be
dynamically loaded implement this common interface we'll have achieved one of our goals – to have the compiler
enforce standardized names of methods and properties across all controls.
Recall that without interfaces, calling a method from the dynamically-loaded control required a switch
structure that hard-coded each possible option. For each option we had to individually select the specific User Control's
property. Also, the IntelliSense feature in Visual Studio .NET would show us all of the properties of each individual
User Control, as shown in the screenshot below:
When using interfaces, however, our switch statement disappears. Instead, we can reference the dynamically-loaded
User Control as an ISection and access the appropriate property with one line of code:
Furthermore, the IntelliSense in Visual Studio .NET shows just those property and methods for the ISection interface.
Besides these coding benefits, we're able to develop the application faster with interfaces because:
Visual Studio .NET automatically generates the interface stubs in our User Controls
Calling the controls only requires one line of code as opposed to a large switch statement (which needs
to be updated whenever new controls that might be dynamically added are created or when method names change)
Method names for similar functions are guaranteed to be the same, making it quicker to become familiar with the
application's API
Conclusion
Interfaces can be used at every tier of an enterprise application. While this article specifically provides an example in
the presentation layer, you can benefit from interfaces at the business or data-access layers as well. Furthermore the .NET
Framework itself extensively uses interfaces. Some of the common ones are for:
Different data providers -
IDataReader, IDbConncetion, IDbDataAdapter, IDbTransaction
Collections - IDictionary, ICollection, IEnumerator, IEnumerable, and
Serialization (ISerializable), among many others
Interfaces provide a great tool for solving the common problems of quality assurance and
flexibility. Not only are they easier and quicker to develop with, they're also common throughout the
rest of the .NET Framework. A solid understanding of interfaces will benefit your applications at every level.