Referencing static class types 


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



ЗНАЕТЕ ЛИ ВЫ?

Referencing static class types



A namespace-or-type-name (§3.8) is permitted to reference a static class if

· The namespace-or-type-name is the T in a namespace-or-type-name of the form T.I, or

· The namespace-or-type-name is the T in a typeof-expression (§7.5.11) of the form typeof(T).

A primary-expression (§7.5) is permitted to reference a static class if

· The primary-expression is the E in a member-access (§7.5.4) of the form E.I.

In any other context it is a compile-time error to reference a static class. For example, it is an error for a static class to be used as a base class, a constituent type (§10.3.8) of a member, a generic type argument, or a type parameter constraint. Likewise, a static class cannot be used in an array type, a pointer type, a new expression, a cast expression, an is expression, an as expression, a sizeof expression, or a default value expression.

Partial modifier

The partial modifier is used to indicate that this class-declaration is a partial type declaration. Multiple partial type declarations with the same name within an enclosing namespace or type declaration combine to form one type declaration, following the rules specified in §10.2.

Having the declaration of a class distributed over separate segments of program text can be useful if these segments are produced or maintained in different contexts. For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. Textual separation of the two prevents updates by one from conflicting with updates by the other.

Type parameters

A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. A type parameter is a formal placeholder for a type that will be supplied later. By constrast, a type argument (§4.4.1) is the actual type that is substituted for the type parameter when a constructed type is created.

type-parameter-list:
< type-parameters >

type-parameters:
attributesopt type-parameter
type-parameters, attributesopt type-parameter

type-parameter:
identifier

Each type parameter in a class declaration defines a name in the declaration space (§3.3) of that class. Thus, it cannot have the same name as another type parameter or a member declared in that class. A type parameter cannot have the same name as the type itself.

Class base specification

A class declaration may include a class-base specification, which defines the direct base class of the class and the interfaces (§13) directly implemented by the class.

class-base:
: class-type
: interface-type-list
: class-type, interface-type-list

interface-type-list:
interface-type
interface-type-list, interface-type

The base class specified in a class declaration can be a constructed class type (§4.4). A base class cannot be a type parameter on its own, though it can involve the type parameters that are in scope.

class Extend<V>: V {} // Error, type parameter used as base class

Base classes

When a class-type is included in the class-base, it specifies the direct base class of the class being declared. If a class declaration has no class-base, or if the class-base lists only interface types, the direct base class is assumed to be object. A class inherits members from its direct base class, as described in §10.3.3.

In the example

class A {}

class B: A {}

class A is said to be the direct base class of B, and B is said to be derived from A. Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

For a constructed class type, if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each type-parameter in the base class declaration, the corresponding type-argument of the constructed type. Given the generic class declarations

class B<U,V> {...}

class G<T>: B<string,T[]> {...}

the base class of the constructed type G<int> would be B<string,int[]>.

The direct base class of a class type must be at least as accessible as the class type itself (§3.5.2). For example, it is a compile-time error for a public class to derive from a private or internal class.

The direct base class of a class type must not be any of the following types: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, or System.ValueType. Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

While determining the meaning of the direct base class specification A of a class B, the direct base class of B is temporarily assumed to be object. Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. The example:

class A<T> {

public class B{}

}

class C: A<C.B> {}

Is in error since in the base class specification A<C.B> the direct base class of C is considered to be object, and hence (by the rules of §3.8) C is not considered to have a member B.

The base classes of a class type are the direct base class and its base classes. In other words, the set of base classes is the transitive closure of the direct base class relationship. Referring to the example above, the base classes of B are A and object. In the example

class A {...}

class B<T>: A {...}

class C<T>: B<IComparable<T>> {...}

class D<T>: C<T[]> {...}

the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Except for class object, every class type has exactly one direct base class. The object class has no direct base class and is the ultimate base class of all other classes.

When a class B derives from a class A, it is a compile-time error for A to depend on B. A class directly depends on its direct base class (if any) and directly depends on the class within which it is immediately nested (if any). Given this definition, the complete set of classes upon which a class depends is the reflexive and transitive closure of the directly depends on relationship.

The example

class A: A {}

Is erroneous because the class depends on itself. Likewise, the example

class A: B {}

class B: C {}

class C: A {}

is in error because the classes circularly depend on themselves. Finally, the example

class A: B.C {}

class B: A
{
public class C {}
}

results in a compile-time error because A depends on B.C (its direct base class), which depends on B (its immediately enclosing class), which circularly depends on A.

Note that a class does not depend on the classes that are nested within it. In the example

class A
{
class B: A {}
}

B depends on A (because A is both its direct base class and its immediately enclosing class), but A does not depend on B (since B is neither a base class nor an enclosing class of A). Thus, the example is valid.

It is not possible to derive from a sealed class. In the example

sealed class A {}

class B: A {} // Error, cannot derive from a sealed class

class B is in error because it attempts to derive from the sealed class A.

Interface implementations

A class-base specification may include a list of interface types, in which case the class is said to directly implement the given interface types. Interface implementations are discussed further in §13.4.

Type parameter constraints

Generic type and method declarations can optionally specify type parameter constraints by including type-parameter-constraints-clauses.

type-parameter-constraints-clauses:
type-parameter-constraints-clause
type-parameter-constraints-clauses type-parameter-constraints-clause

type-parameter-constraints-clause:
where type-parameter: type-parameter-constraints

type-parameter-constraints:
primary-constraint
secondary-constraints
constructor-constraint
primary-constraint, secondary-constraints
primary-constraint, constructor-constraint
secondary-constraints, constructor-constraint
primary-constraint, secondary-constraints, constructor-constraint

primary-constraint:
class-type
class
struct

secondary-constraints:
interface-type
type-parameter
secondary-constraints, interface-type
secondary-constraints, type-parameter

constructor-constraint:
new ()

Each type-parameter-constraints-clause consists of the token where, followed by the name of a type parameter, followed by a colon and the list of constraints for that type parameter. There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Like the get and set tokens in a property accessor, the where token is not a keyword.

The list of constraints given in a where clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, new().

A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. A secondary constraint can be a type-parameter or interface-type.

The reference type constraint specifies that a type argument used for the type parameter must be a reference type. All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable type (§4.1.10) does not satisfy the value type constraint. A type parameter having the value type constraint cannot also have the constructor-constraint.

Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

If a constraint is a class type, an interface type, or a type parameter, that type specifies a minimal “base type” that every type argument used for that type parameter must support. Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. The type argument supplied must satisfy the conditions described in section 4.4.4.

A class-type constraint must satisfy the following rules:

· The type must be a class type.

· The type must not be sealed.

· The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.

· The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.

· At most one constraint for a given type parameter can be a class type.

A type specified as an interface-type constraint must satisfy the following rules:

· The type must be an interface type.

· A type must not be specified more than once in a given where clause.

In either case, the constraint can involve any of the type parameters of the associated type or method declaration as part of a constructed type, and can involve the type being declared.

Any class or interface type specified as a type parameter constraint must be at least as accessible (§3.5.4) as the generic type or method being declared.

A type specified as a type-parameter constraint must satisfy the following rules:

· The type must be a type parameter.

· A type must not be specified more than once in a given where clause.

In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

· If a type parameter T is used as a constraint for type parameter S then S depends on T.

· If a type parameter S depends on a type parameter T and T depends on a type parameter U then S depends on U.

Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Any constraints must be consistent among dependent type parameters. If type parameter S depends on type parameter T then:

· T must not have the value type constraint. Otherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.

· If S has the value type constraint then T must not have a class-type constraint.

· If S has a class-type constraint A and T has a class-type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

· If S also depends on type parameter U and U has a class-type constraint A and T has a class-type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

It is valid for S to have the value type constraint and T to have the reference type constraint. Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

If the where clause for a type parameter includes a constructor constraint (which has the form new()), it is possible to use the new operator to create instances of the type (§7.6.10.1). Any type argument used for a type parameter with a constructor constraint must have a public parameterless constructor (this constructor implicitly exists for any value type) or be a type parameter having the value type constraint or constructor constraint (see §10.1.5 for details).

The following are examples of constraints:

interface IPrintable
{
void Print();
}

interface IComparable<T>
{
int CompareTo(T value);
}

interface IKeyProvider<T>
{

T GetKey();
}

class Printer<T> where T: IPrintable {...}

class SortedList<T> where T: IComparable<T> {...}

class Dictionary<K,V>
where K: IComparable<K>
where V: IPrintable, IKeyProvider<K>, new()
{
...
}

The following example is in error because it causes a circularity in the dependency graph of the type parameters:

class Circular<S,T>
where S: T
where T: S // Error, circularity in dependency graph
{
...
}

The following examples illustrate additional invalid situations:

class Sealed<S,T>
where S: T
where T: struct // Error, T is sealed
{
...
}

class A {...}

class B {...}

class Incompat<S,T>
where S: A, T
where T: B // Error, incompatible class-type constraints
{
...
}

class StructWithClass<S,T,U>
where S: struct, T
where T: U
where U: A // Error, A incompatible with struct
{
...
}

The effective base class of a type parameter T is defined as follows:

· If T has no primary constraints or type parameter constraints, its effective base class is object.

· If T has the value type constraint, its effective base class is System.ValueType.

· If T has a class-type constraint C but no type-parameter constraints, its effective base class is C.

· If T has no class-type constraint but has one or more type-parameter constraints, its effective base class is the most encompassed type (§6.4.2) in the set of effective base classes of its type-parameter constraints. The consistency rules ensure that such a most encompassed type exists.

· If T has both a class-type constraint and one or more type-parameter constraints, its effective base class is the most encompassed type (§6.4.2) in the set consisting of the class-type constraint of T and the effective base classes of its type-parameter constraints. The consistency rules ensure that such a most encompassed type exists.

· If T has the reference type constraint but no class-type constraints, its effective base class is object.

For the purpose of these rules, if T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type. This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method.

These rules ensure that the effective base class is always a class-type.

The effective interface set of a type parameter T is defined as follows:

· If T has no secondary-constraints, its effective interface set is empty.

· If T has interface-type constraints but no type-parameter constraints, its effective interface set is its set of interface-type constraints.

· If T has no interface-type constraints but has type-parameter constraints, its effective interface set is the union of the effective interface sets of its type-parameter constraints.

· If T has both interface-type constraints and type-parameter constraints, its effective interface set is the union of its set of interface-type constraints and the effective interface sets of its type-parameter constraints.

A type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object or System.ValueType.

Values of a constrained type parameter type can be used to access the instance members implied by the constraints. In the example

interface IPrintable
{
void Print();
}

class Printer<T> where T: IPrintable
{
void PrintOne(T x) {
x.Print();
}
}

the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Class body

The class-body of a class defines the members of that class.

class-body:
{ class-member-declarationsopt }

Partial types

A type declaration can be split across multiple partial type declarations. The type declaration is constructed from its parts by following the rules in this section, whereupon it is treated as a single declaration during the remainder of the compile-time and run-time processing of the program.

A class-declaration, struct-declaration or interface-declaration represents a partial type declaration if it includes a partial modifier. partial is not a keyword, and only acts as a modifier if it appears immediately before one of the keywords class, struct or interface in a type declaration, or before the type void in a method declaration. In other contexts it can be used as a normal identifier.

Each part of a partial type declaration must include a partial modifier. It must have the same name and be declared in the same namespace or type declaration as the other parts. The partial modifier indicates that additional parts of the type declaration may exist elsewhere, but the existence of such additional parts is not a requirement; it is valid for a type with a single declaration to include the partial modifier.

All parts of a partial type must be compiled together such that the parts can be merged at compile-time into a single type declaration. Partial types specifically do not allow already compiled types to be extended.

Nested types may be declared in multiple parts by using the partial modifier. Typically, the containing type is declared using partial as well, and each part of the nested type is declared in a different part of the containing type.

The partial modifier is not permitted on delegate or enum declarations.

Attributes

The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. For example, the two parts:

[Attr1, Attr2("hello")]
partial class A {}

[Attr3, Attr2("goodbye")]
partial class A {}

are equivalent to a declaration such as:

[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}

Attributes on type parameters combine in a similar fashion.

Modifiers

When a partial type declaration includes an accessibility specification (the public, protected, internal, and private modifiers) it must agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (§3.5.1).

If one or more partial declarations of a nested type include a new modifier, no warning is reported if the nested type hides an inherited member (§3.7.1.2).

If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (§10.1.1.1). Otherwise, the class is considered non-abstract.

If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (§10.1.1.2). Otherwise, the class is considered unsealed.

Note that a class cannot be both abstract and sealed.

When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (§18.1).



Поделиться:


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

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