Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   More About Calling Interface Methods

The FCL’s System.String type inherits System.Object’s method signatures and their imple- mentations. In addition, the String type also implements several interfaces: IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<Char>, and IEquatable<String>. This means that the String type isn’t required to implement (or override) the methods its Object base type offers. However, the String type must implement the methods declared in all of the interfaces.

The CLR allows you to define field, parameter, or local variables that are of an interface type. Using a variable of an interface type allows you to call methods defined by that interface. In addition, the CLR will allow you to call methods defined by Object because all classes inherit Object’s methods. The following code demonstrates this.

 

// The s variable refers to a String object. String s = "Jeffrey";

// Using s, I can call any method defined in

// String, Object, IComparable, ICloneable, IConvertible, IEnumerable, etc.

 

// The cloneable variable refers to the same String object ICloneable cloneable = s;


// Using cloneable, I can call any method declared by the

// ICloneable interface (or any method defined by Object) only.

 

// The comparable variable refers to the same String object IComparable comparable = s;

// Using comparable, I can call any method declared by the

// IComparable interface (or any method defined by Object) only.

 

// The enumerable variable refers to the same String object

// At run time, you can cast a variable from one interface to another as

// long as the object's type implements both interfaces. IEnumerable enumerable = (IEnumerable) comparable;

// Using enumerable, I can call any method declared by the

// IEnumerable interface (or any method defined by Object) only.

 

In this code, all of the variables refer to the same “Jeffrey” String object that is in the managed heap, and therefore, any method that I call while using any of these variables applies to the one “Jeffrey” String object. However, the type of the variable indicates the action that I can perform on the object. The s variable is of type String, and therefore, I can use s to call any members defined by the String type (such as the Length property). I can also use the variable s to call any methods inherited from Object (such as GetType).

The cloneable variable is of the ICloneable interface type, and therefore, using the cloneable variable, I can call the Clone method defined by this interface. In addition, I can call any method defined by Object (such as GetType) because the CLR knows that all types derive from Object.

However, using the cloneable variable, I cannot call public methods defined by String itself or any methods defined by any other interface that String implements. Similarly, using the comparable variable, I can call CompareTo or any method defined by Object, but no other methods are callable using this variable.



       
   
 
 

 

 

 
 

Implicit and Explicit Interface Method Implementations (What’s Happening Behind the Scenes)

When a type is loaded into the CLR, a method table is created and initialized for the type (as dis- cussed in Chapter 1, “The CLR’s Execution Model”). This method table contains one entry for every new method introduced by the type as well as entries for any virtual methods inherited by the type.


Inherited virtual methods include methods defined by the base types in the inheritance hierarchy as well as any methods defined by the interface types. So if you have a simple type defined like this:

 

internal sealed class SimpleType : IDisposable {

public void Dispose() { Console.WriteLine("Dispose"); }

}

 

the type’s method table contains entries for the following:

 

■ All the virtual instance methods defined by Object, the implicitly inherited base class.

 

■ All the interface methods defined by IDisposable, the inherited interface. In this example, there is only one method, Dispose, because the IDisposable interface defines just one method.

■ The new method, Dispose, introduced by SimpleType.

 

To make things simple for the programmer, the C# compiler assumes that the Dispose method introduced by SimpleType is the implementation for IDisposable’s Dispose method. The C# compiler makes this assumption because the method is public, and the signatures of the interface method and the newly introduced method are identical. That is, the methods have the same pa- rameter and return types. By the way, if the new Dispose method were marked as virtual, the C# compiler would still consider this method to be a match for the interface method.

When the C# compiler matches a new method to an interface method, it emits metadata indicat- ing that both entries in SimpleType’s method table should refer to the same implementation. To help make this clearer, here is some code that demonstrates how to call the class’s public Dispose method as well as how to call the class’s implementation of IDisposable’s Dispose method.

 

public sealed class Program { public static void Main() {

SimpleType st = new SimpleType();

 

// This calls the public Dispose method implementation st.Dispose();

 

// This calls IDisposable's Dispose method implementation IDisposable d = st;

d.Dispose();

}

}

 

In the first call to Dispose, the Dispose method defined by SimpleType is called. Then I de- fine a variable, d, which is of the IDisposable interface type. I initialize the d variable to refer to the SimpleType object. Now when I call d.Dispose(), I am calling the IDisposable interface’s

Dispose method. Because C# requires the public Dispose method to also be the implementation for IDisposable’s Dispose method, the same code will execute, and, in this example, you can’t see any observable difference. The output is as follows.

 

Dispose Dispose


Now, let me rewrite the preceding SimpleType so that you can see an observable difference.

 

internal sealed class SimpleType : IDisposable {

public void Dispose() { Console.WriteLine("public Dispose"); }

void IDisposable.Dispose() { Console.WriteLine("IDisposable Dispose"); }

}

 

Without changing the Main method shown earlier, if we just recompile and rerun the program, the output will be the following.

 

public Dispose IDisposable Dispose

 

In C#, when you prefix the name of a method with the name of the interface that defines the method (IDisposable.Dispose as in this example), you are creating an explicit interface method implementation (EIMI). Note that when you define an explicit interface method in C#, you are not allowed to specify any accessibility (such as public or private). However, when the compiler generates the metadata for the method, its accessibility is set to private, preventing any code using an instance of the class from simply calling the interface method. The only way to call the interface method is through a variable of the interface’s type.

Also note that an EIMI method cannot be marked as virtual and therefore cannot be overridden. This is because the EIMI method is not really part of the type’s object model; it’s a way of attaching an interface (set of behaviors or methods) onto a type without making the behaviors/methods obvious. If all of this seems a bit kludgy to you, you are understanding it correctly—this is all a bit kludgy. Later in this chapter, I’ll show some valid reasons for using EIMIs.

 

 


Date: 2016-03-03; view: 651


<== previous page | next page ==>
Nbsp;   Inheriting an Interface | Nbsp;   Generic Interfaces
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.011 sec.)