Using Interfaces

In C#, you can't inherit from multiple base classes at the same time. For example, if Window and Graphics are classes, deriving a class named Menu from them like this is illegal:

 
 public class Menu : Window, Graphics   // Illegal 

However, like Java, C# supports Java-style interfaces , which C# treats as a contract, indicating that a class will implement a certain set of properties, methods, events, and/or indexers. Unlike base classes, interfaces don't implement any of their members by providing the actual code for properties, methods , and so on. Interfaces are just specifications for those members , and if your class implements an interface, it must provide code for all the members of that interface. A single class can implement as many interfaces as it likes at the same time (as long as there isn't an unhandled name clash among members), so interfaces are as close as C# comes to implementing multiple inheritance.

FOR C++ PROGRAMMERS

Unlike C++, C# supports Java-style interfaces, so this entire topic is C#-only.


MULTIPLE INHERITANCE VERSUS ABSTRACT CLASSES

Although interfaces are traditionally thought of in terms of a simplified version of multiple inheritance, it might be better to think of them as an extension of abstract classes. They're similar to abstract classes, whose members must be specifically implemented, except that you can implement multiple interfaces at once, whereas you can inherit from only one abstract class at a time.


To create an interface, you use the interface statement:

 
 [  attributes  ] [  modifiers  ] interface  identifier  [:  base-list  ] {  interface-body  }[;] 

Here are the parts of this statement:

  • attributes (Optional) Hold additional declarative information, as we'll see in Chapter 14, "Using Attributes and Reflection."

  • modifiers (Optional) The allowed modifiers are new and a valid combination of the four access modifiers.

  • identifier The interface name.

  • base-list (Optional) A list that contains one or more explicit base interfaces separated by commas.

  • interface-body Declarations of the interface members.

Implementing Interfaces

Say, for example, that you wanted to make sure that the Window class implemented both an Open and a Close method. To do that, you could make that class implement two interfaces, IOpenable , which has an Open method, and ICloseable , which has a Close method (you can add as many members to an interface as you like, of course, not just one):

 
 interface IOpenable {   void Open(); } interface ICloseable {   void Close(); } 

Now you can indicate that your Window class will implement both IOpenable and ICloseable like this (both classes and structs can implement interfaces):

 
  public class Window : IOpenable, ICloseable  {     .     .     .   } } 

You have to implement the members of these interfaces as well:

 
 public class Window : IOpenable, ICloseable {  public void Open()   {   System.Console.WriteLine("Opening...");   }   public void Close()   {   System.Console.WriteLine("Closing...");   }  } 

Now you can create new objects of the Window class and call that object's Open and Close methods, as you see in ch04_09.cs, Listing 4.9.

Listing 4.9 Implementing Interfaces (ch04_09.cs)
 public class ch04_09 {   static void Main()   {  Window window = new Window();   window.Open();   window.Close();  } } interface IOpenable {   void Open(); } interface ICloseable {   void Close(); } public class Window : IOpenable, ICloseable {   public void Open()   {    System.Console.WriteLine("Opening...");   }   public void Close()   {    System.Console.WriteLine("Closing...");   } } 

Here's what you see when you run ch04_09.cs. Note that both Open and Close work as we've implemented them:

 
 C:\>ch04_09 Opening... Closing... 

Deriving New Interfaces from Current Interfaces

You can derive new interfaces just as you do with classes; for example, you can base the IOpenable interface on an interface named IComponent :

 
 interface IOpenable : IComponent {   void Open(); } 

Now a class or struct that implements IOpenable has to implement all the members of the IOpenable and IComponent interfaces. You can also combine interfaces by having an interface implement multiple interfaces like this, where IOpenable implements both the IComponent and IBase interfaces:

 
 interface IOpenable : IComponent, IBase { } 

As you'd expect, however, you cannot create an object from an interface directly. Because an interface only specifies members and doesn't implement them, all the code you need for those members would be missing. However, you can use a cast to create an instance of an interface in C#. For example, you can cast the window object in ch04_09.cs to an instance of IOpenable using the cast (IOpenable) . Because you've created this instance by casting the window object, you can then call its Open method (which is implemented in the Window class):

 
 static void Main() {   Window window = new Window();   window.Open();   window.Close();  IOpenable iwindow = (IOpenable) window;   iwindow.Open();  } 

If you added the highlighted code here to ch04_09.cs, you'd see this new result:

 
 C:\>ch04_09 Opening... Closing... Opening... 

USING INTERFACE INSTANCES WITH VALUE TYPES

Assume for example that you implement an interface in a struct that is, a value type. When you create an interface instance using a variable of that struct type, there's an implicit boxing that goes on, creating an object. If you modify the data in the object, bear in mind that you won't be modifying the data in the original variable.


Checking Interface Types

It can be a good idea to determine whether an object supports a particular interface before trying to call that interface's methods. There are several ways of determining which interfaces an object supports at runtime.

For example, you have an interface named IOpenable and the Window class implements that interface:

 
 interface IOpenable {   void Open(); } public class Window : IOpenable {   public void Open()   {    System.Console.WriteLine("Opening...");   } } 

You now create a Window object named window and want to determine whether it supports the IOpenable interface. One way to do that is with the is keyword, which lets you know whether an object is a certain type. This keyword returns a bool value, true or false , so we can use it like this to determine whether window implements the IOpenable interface. Note that if window does implement the interface, we call its Open method, but not otherwise :

 
 if (window is IOpenable){   window.Open(); } 

Alternatively, you can try to cast the window object to an instance of the IOpenable interface, and if there's no cast exception ( System.InvalidCastException ), you can call the Open method as before:

 
 IOpenable iwindow; try {   iwindow = window as IOpenable;   iwindow.Open(); } catch(System.InvalidCastException) {   System.Console.WriteLine("Casting exception."); } 

In fact, there's an easier way to try a cast to IOpenable without having to handle cast exceptions. You can use the as keyword to cast window to the IOpenable interface instance iwindow ; if iwindow ends up being null, the cast wasn't successful, but no illegal cast exception occurs. You can see this and the other two methods of checking interface implementations in ch04_10.cs, Listing 4.10.

Listing 4.10 Checking Interface Types (ch04_10.cs)
 public class ch04_10 {   static void Main()   {    Window window = new Window();    IOpenable iwindow;    if (window is IOpenable){      window.Open();    }    try    {      iwindow = window as IOpenable;      iwindow.Open();    }    catch(System.InvalidCastException)    {      System.Console.WriteLine("Casting exception.");    }  iwindow = window as IOpenable;   if(iwindow != null){   window.Open();   }  } } interface IOpenable {   void Open(); } public class Window : IOpenable {   public void Open()   {    System.Console.WriteLine("Opening...");   } } 

Overriding Implemented Members

You can also make your implementation of an interface method virtual, meaning you can override it later. You can see an example in ch04_11.cs (Listing 4.11); there, the Window class implements the IOpenable interface's Open method as a virtual method. The Menu class is based on the Window class, and overrides the Open method with its own version.

IS VERSUS AS

It turns out that the is operator is not efficiently implemented internally; as is slightly more efficient, thus saving your code a little time.


Listing 4.11 Overriding Implemented Members (ch04_11.cs)
 public class ch04_11 {   static void Main()   {  Window window = new Window();   window.Open();   Menu menu = new Menu();   menu.Open();  } } interface IOpenable {   void Open(); } public class Window : IOpenable {  public virtual void Open()  {    System.Console.WriteLine("Opening...");   } } public class Menu : Window {  public override void Open()  {    System.Console.WriteLine("Displaying items...");   } } 

Here's what you see when you run ch04_11.cs:

 
 C:\>ch04_11 Opening... Displaying items... 

Note that you don't have to use a virtual / override pair as was done in ch04_11.cs. You can simply use new to create a new version of Open if you prefer.

Resolving Member Conflicts

What if you're implementing interfaces and two members clash by having the same name? For example, say you have two interfaces like these, both with an Open method:

 
 interface IOpenable {   void Open(); } interface IComponent {   void Open(); } 

How can you implement both interfaces and resolve the conflict here? You can do that with explicit implementation , where you indicate that you're explicitly implementing an interface's method. For example, to explicitly implement the IComponent.Open method, you can use code like this (explicitly implemented methods can't be declared with the abstract , new , virtual , or override keywords):

 
 public class Window : IOpenable, IComponent {  void IComponent.Open()  {     System.Console.WriteLine("IComponent.Open opening...");   }     .     .     . } 

Now you can implement the IOpenable interface's Open method without needing to qualify it as IOpenable.Open . You can just define it as Open :

 
 public class Window : IOpenable, IComponent {   void IComponent.Open()   {     System.Console.WriteLine("IComponent.Open opening...");   }  public void Open()   {   System.Console.WriteLine("IOpenable.Open opening...");   }  } 

This makes IOpenable 's version of Open the implicit version that will be called when you use objects of the Window class:

 
 Window window = new Window(); window.Open();  //Calls IOpenable.Open 

On the other hand, you can also call IComponent 's version of Open using an object of the Window class, but it takes a little more work. In this case, you have to create an instance of the IComponent class, which we'll call iwindow , and call iwindow.Open , as you see in ch04_12.cs, Listing 4.12. That's how you reach an explicitly implemented method.

Listing 4.12 Resolving Member Conflicts (ch04_12.cs)
 public class ch04_12 {   static void Main()   {     Window window = new Window();     window.Open();  IComponent iwindow = window as IComponent;   if (iwindow != null)   {   iwindow.Open();   }  } } interface IOpenable {   void Open(); } interface IComponent {   void Open(); } public class Window : IOpenable, IComponent {   void IComponent.Open()   {     System.Console.WriteLine("IComponent.Open opening...");   }   public void Open()   {     System.Console.WriteLine("IOpenable.Open opening...");   } } 

Here's what you see when you run ch04_12.cs. Note that we were able to reach both the implicit and explicit Open method:

 
 C:\>ch04_12 IOpenable.Open opening... IComponent.Open opening... 

You can also have conflicts if you base one interface on another. For example, take a look at this code, where IOpenable inherits IComponent , and both have an Open method:

 
 interface IComponent {   void Open(); } interface IOpenable : IComponent {   void new Open(); } 

In this case, to avoid conflicts, you have to implement one Open method or the otheror bothexplicitly. Here are your options in a class that implements the IOpenable class:

 
 public class Window : IOpenable {  void IComponent.Open(){}   public void Open(){}  } public class Window : IOpenable {  void Open(){}   public void IOpenable.Open(){}  } public class Window : IOpenable {  void IComponent.Open(){}   public void IOpenable.Open(){}  } 


Microsoft Visual C#. NET 2003 Kick Start
Microsoft Visual C#.NET 2003 Kick Start
ISBN: 0672325470
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net