Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Uniqueness of implemented interfaces

Поиск

The interfaces implemented by a generic type declaration must remain unique for all possible constructed types. Without this rule, it would be impossible to determine the correct method to call for certain constructed types. For example, suppose a generic class declaration were permitted to be written as follows:

interface I<T>
{
void F();
}

class X<U,V>: I<U>, I<V> // Error: I<U> and I<V> conflict
{
void I<U>.F() {...}
void I<V>.F() {...}
}

Were this permitted, it would be impossible to determine which code to execute in the following case:

I<int> x = new X<int,int>();
x.F();

To determine if the interface list of a generic type declaration is valid, the following steps are performed:

· Let L be the list of interfaces directly specified in a generic class, struct, or interface declaration C.

· Add to L any base interfaces of the interfaces already in L.

· Remove any duplicates from L.

· If any possible constructed type created from C would, after type arguments are substituted into L, cause two interfaces in L to be identical, then the declaration of C is invalid. Constraint declarations are not considered when determining all possible constructed types.

In the class declaration X above, the interface list L consists of I<U> and I<V>. The declaration is invalid because any constructed type with U and V being the same type would cause these two interfaces to be identical types.

It is possible for interfaces specified at different inheritance levels to unify:

interface I<T>
{
void F();
}

class Base<U>: I<U>
{
void I<U>.F() {…}
}

class Derived<U,V>: Base<U>, I<V> // Ok
{
void I<V>.F() {…}
}

This code is valid even though Derived<U,V> implements both I<U> and I<V>. The code

I<int> x = new Derived<int,int>();
x.F();

invokes the method in Derived, since Derived<int,int> effectively re-implements I<int> (§13.4.6).

Implementation of generic methods

When a generic method implicitly implements an interface method, the constraints given for each method type parameter must be equivalent in both declarations (after any interface type parameters are replaced with the appropriate type arguments), where method type parameters are identified by ordinal positions, left to right.

When a generic method explicitly implements an interface method, however, no constraints are allowed on the implementing method. Instead, the constraints are inherited from the interface method

interface I<A,B,C>
{
void F<T>(T t) where T: A;
void G<T>(T t) where T: B;
void H<T>(T t) where T: C;
}

class C: I<object,C,string>
{
public void F<T>(T t) {...} // Ok
public void G<T>(T t) where T: C {...} // Ok
public void H<T>(T t) where T: string {...} // Error
}

The method C.F<T> implicitly implements I<object,C,string>.F<T>. In this case, C.F<T> is not required (nor permitted) to specify the constraint T: object since object is an implicit constraint on all type parameters. The method C.G<T> implicitly implements I<object,C,string>.G<T> because the constraints match those in the interface, after the interface type parameters are replaced with the corresponding type arguments. The constraint for method C.H<T> is an error because sealed types (string in this case) cannot be used as constraints. Omitting the constraint would also be an error since constraints of implicit interface method implementations are required to match. Thus, it is impossible to implicitly implement I<object,C,string>.H<T>. This interface method can only be implemented using an explicit interface member implementation:

class C: I<object,C,string>
{
...

public void H<U>(U u) where U: class {...}

void I<object,C,string>.H<T>(T t) {
string s = t; // Ok
H<T>(t);
}
}

In this example, the explicit interface member implementation invokes a public method having strictly weaker constraints. Note that the assignment from t to s is valid since T inherits a constraint of T: string, even though this constraint is not expressible in source code.

Interface mapping

A class or struct must provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as interface mapping.

Interface mapping for a class or struct C locates an implementation for each member of each interface specified in the base class list of C. The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:

· If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.

· Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M. If more than one member matches, it is unspecified which member is the implementation of I.M. This situation can only occur if S is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.

A compile-time error occurs if implementations cannot be located for all members of all interfaces specified in the base class list of C. Note that the members of an interface include those members that are inherited from base interfaces.

For purposes of interface mapping, a class member A matches an interface member B when:

· A and B are methods, and the name, type, and formal parameter lists of A and B are identical.

· A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

· A and B are events, and the name and type of A and B are identical.

· A and B are indexers, the type and formal parameter lists of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

Notable implications of the interface mapping algorithm are:

· Explicit interface member implementations take precedence over other members in the same class or struct when determining the class or struct member that implements an interface member.

· Neither non-public nor static members participate in interface mapping.

In the example

interface ICloneable
{
object Clone();
}

class C: ICloneable
{
object ICloneable.Clone() {...}

public object Clone() {...}
}

the ICloneable.Clone member of C becomes the implementation of Clone in ICloneable because explicit interface member implementations take precedence over other members.

If a class or struct implements two or more interfaces containing a member with the same name, type, and parameter types, it is possible to map each of those interface members onto a single class or struct member. For example

interface IControl
{
void Paint();
}

interface IForm
{
void Paint();
}

class Page: IControl, IForm
{
public void Paint() {...}
}

Here, the Paint methods of both IControl and IForm are mapped onto the Paint method in Page. It is of course also possible to have separate explicit interface member implementations for the two methods.

If a class or struct implements an interface that contains hidden members, then some members must necessarily be implemented through explicit interface member implementations. For example

interface IBase
{
int P { get; }
}

interface IDerived: IBase
{
new int P();
}

An implementation of this interface would require at least one explicit interface member implementation, and would take one of the following forms

class C: IDerived
{
int IBase.P { get {...} }

int IDerived.P() {...}
}

class C: IDerived
{
public int P { get {...} }

int IDerived.P() {...}
}

class C: IDerived
{
int IBase.P { get {...} }

public int P() {...}
}

When a class implements multiple interfaces that have the same base interface, there can be only one implementation of the base interface. In the example

interface IControl
{
void Paint();
}

interface ITextBox: IControl
{
void SetText(string text);
}

interface IListBox: IControl
{
void SetItems(string[] items);
}

class ComboBox: IControl, ITextBox, IListBox
{
void IControl.Paint() {...}

void ITextBox.SetText(string text) {...}

void IListBox.SetItems(string[] items) {...}
}

it is not possible to have separate implementations for the IControl named in the base class list, the IControl inherited by ITextBox, and the IControl inherited by IListBox. Indeed, there is no notion of a separate identity for these interfaces. Rather, the implementations of ITextBox and IListBox share the same implementation of IControl, and ComboBox is simply considered to implement three interfaces, IControl, ITextBox, and IListBox.

The members of a base class participate in interface mapping. In the example

interface Interface1
{
void F();
}

class Class1
{
public void F() {}

public void G() {}
}

class Class2: Class1, Interface1
{
new public void G() {}
}

the method F in Class1 is used in Class2's implementation of Interface1.



Поделиться:


Последнее изменение этой страницы: 2016-08-10; просмотров: 267; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.12.34.96 (0.009 с.)