Interface-Based Programming - Working with Interfaces
(Page 4 of 4 )
Now that you have learned the importance of using interfaces in your component-based application, it’s time to examine a number of practical issues regarding working with interfaces and tying them to the rest of your application. Later in this chapter, you will also see the support Visual Studio 2005 offers component developers when it comes to adding implementation to your classes for predefined interfaces.
When you name a new interface type, you should prefix it with a capitalIand capitalize the first letter of the domain term, as inIAccount,IController,ICalculator, and so on. Use theIprefix even if the domain term itself starts with anI(such as inIIDentityor IImage). .NET tries to do away with the old Windows and C++ Hungarian naming notations (that is, prefixing a variable name with its type), but theIprefix is a direct legacy from COM, and that tradition is maintained in .NET.
Interfaces and Type Safety Interfaces are abstract types and, as such, can’t be used directly. To use an interface, you need to cast into an interface reference an object that supports it. There are two types of casting—implicit and explicit—and which type you use has an impact on type safety.
Assigning a class instance to an interface variable directly is called an implicit cast, because the compiler is required to figure out which type to cast the class to:
IMyInterface obj;
obj = new MyClass();
obj.Method1();
When you use implicit casts, the compiler enforces type safety. If the classMyClassdoesn’t implement theIMyInterfaceinterface, the compiler refuses to generate the code and produces a compilation error. The compiler can do that because it can read the class’s metadata and can tell in advance that the class doesn’t derive from the interface. However, there are a number of cases where you cannot use implicit
casting. In such cases, you can use explicit cast instead. Explicit casting means casting one type to another type:
IMyInterface obj;
/* Some code here */
obj = (IMyInterface)new MyClass();
obj.Method1();
However, bear in mind that explicit casts to an interface are made at the expense of type safety. Even if the class doesn’t support the interface, the compiler will compile the client’s code, and .NET will throw an exception at runtime when the cast fails.
An example where implicit cast is unavailable is when dealing with non-generic class factories. In object-oriented programming, clients often don’t create objects directly, but rather get their instances from a class factory—a known object in the system that clients ask to create objects they require, instead of creating them directly.* The advantage of using a class factory is that only the factory is coupled to the actual component types that provide the interfaces. The clients only know about the intefaces. When you need to switch from one service provider to another you only need to modify the factory (actually, instantiate a different type of a factory); the clients aren’t affected. When using a class factory that returns some common base type (usuallyobject), you can use an explicit cast from the returnedobjectto the interface type:
public interface IClassFactory
{
object GetObject();
}
IClassFactory factory;
/* Some code to initialize the class factory */
IMyInterface obj;
obj = (IMyInterface)factory.GetObject();
obj.Method1();
When using generics (where the type parameter is defined at the factory or the method level), there is no need for the cast:
public interface IClassFactory<T>
{
T GetObject();
}
IClassFactory<IMyInterface> factory;
/* Some code to initialize the class factory */
IMyInterface obj;
obj = factory.GetObject();
obj.Method1();
Generic interfaces are discussed in more detail later in this chapter.
Another example where implicit cast is impossible is when you want to use one interface that the class implements to get a reference to another interface that the class also supports. Consider the code in Example 3-5. Even when the client uses an implicit cast to get hold of the first interface, it needs an explicit cast to obtain the second.
Example 3-5. Defining and using multiple interfaces
public interface IMyInterface
{
void Method1();
void Method2();
}
public interface IMyOtherInterface
{
void Method3();
}
public class MyClass : IMyInterface,IMyOtherInterface
{
public void Method1()
{...}
public void Method2()
{...}
public void Method3()
{...}
}
//Client-side code:
IMyInterface obj1;
IMyOtherInterface obj2;
obj1 = new MyClass();
obj1.Method1();
obj2 = (IMyOtherInterface)obj1; obj2.Method3();
In all these examples that use explicit casts, you must incorporate error handling, in case the type you are trying to cast from doesn’t support the interface, and usetryandcatch statements to handle any exceptions.
There is, however, a safer, defensive approach to explicit casting-theas operator. Theasoperator performs the cast if it’s legal and assigns a value to the variable. If a cast isn’t possible, instead of throwing an exception, theasoperator assignsnullto the interface variable. Example 3-6 shows how to use theasoperator to perform a safe cast that doesn’t result in an exception in case of an error.
Example 3-6. Using the as operator to cast safely to the desired interface
SomeType obj1;
IMyInterface obj2;
/* Some code to initialize obj1 */
obj2 = obj1 as IMyInterface;
if(obj2 != null)
{
obj.Method1();
}
else
{
//Handle error in expected interface
}
Interestingly enough, using theasoperator to find out whether a particular object supports a given interface is semantically identical to COM’sQueryInterface()method. Both mechanisms allow clients to defensively obtain an interface from an object and handle the situation where the interface isn’t supported.
In general, you should always program defensively on the client side, using theas operator as shown in Example 3-6, instead of explicit casting. Never assume an object supports an interface—that leads both to robust error handling and to separation of the interface from the implementation, regardless of whether or not the server is using explicit interface implementation. Make it a habit on the client side to use the server via an interface and thus enforce the separation manually.
Please check back next week for the continuation of this article.
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |
|
This article is excerpted from chapter three of Programming .NET Components, Second Edition, written by Juval Lowy (O'Reilly, 2006; ISBN: 0596007620). Check it out today at your favorite bookstore. Buy this book now.
|
|