Members of constructed types 


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



ЗНАЕТЕ ЛИ ВЫ?

Members of constructed types



The non-inherited members of a constructed type are obtained by substituting, for each type-parameter in the member declaration, the corresponding type-argument of the constructed type. The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

For example, given the generic class declaration

class Gen<T,U>
{
public T[,] a;

public void G(int i, T t, Gen<U,T> gt) {...}

public U Prop { get {...} set {...} }

public int H(double d) {...}
}

the constructed type Gen<int[],IComparable<string>> has the following members:

public int[,][] a;

public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {...}

public IComparable<string> Prop { get {...} set {...} }

public int H(double d) {...}

The type of the member a in the generic class declaration Gen is “two-dimensional array of T”, so the type of the member a in the constructed type above is “two-dimensional array of one-dimensional array of int”, or int[,][].

Within instance function members, the type of this is the instance type (§10.3.1) of the containing declaration.

All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. When a particular closed constructed type (§4.4.2) is used at run-time, each use of a type parameter is replaced with the actual type argument supplied to the constructed type. For example:

class C<V>
{
public V f1;
public C<V> f2 = null;

public C(V x) {
this.f1 = x;
this.f2 = this;
}
}

class Application
{
static void Main() {
C<int> x1 = new C<int>(1);
Console.WriteLine(x1.f1); // Prints 1

C<double> x2 = new C<double>(3.1415);
Console.WriteLine(x2.f1); // Prints 3.1415
}
}

Inheritance

A class inherits the members of its direct base class type. Inheritance means that a class implicitly contains all members of its direct base class type, except for the instance constructors, destructors and static constructors of the base class. Some important aspects of inheritance are:

· Inheritance is transitive. If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.

· A derived class extends its direct base class. A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.

· Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (§3.5). However, depending on their declared accessibility, inherited members might not be accessible in a derived class.

· A derived class can hide (§3.7.1.2) inherited members by declaring new members with the same name or signature. Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.

· An instance of a class contains a set of all instance fields declared in the class and its base classes, and an implicit conversion (§6.1.6) exists from a derived class type to any of its base class types. Thus, a reference to an instance of some derived class can be treated as a reference to an instance of any of its base classes.

· A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation varies depending on the run-time type of the instance through which that function member is invoked.

The inherited member of a constructed class type are the members of the immediate base class type (§10.1.4.1), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the base-class-specification. These members, in turn, are transformed by substituting, for each type-parameter in the member declaration, the corresponding type-argument of the base-class-specification.

class B<U>
{
public U F(long index) {...}
}

class D<T>: B<T[]>
{
public T G(string s) {...}
}

In the above example, the constructed type D<int> has a non-inherited member public int G(string s) obtained by substituting the type argument int for the type parameter T. D<int> also has an inherited member from the class declaration B. This inherited member is determined by first determining the base class type B<int[]> of D<int> by substituting int for T in the base class specification B<T[]>. Then, as a type argument to B, int[] is substituted for U in public U F(long index), yielding the inherited member public int[] F(long index).

The new modifier

A class-member-declaration is permitted to declare a member with the same name or signature as an inherited member. When this occurs, the derived class member is said to hide the base class member. Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. To suppress the warning, the declaration of the derived class member can include a new modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in §3.7.1.2.

If a new modifier is included in a declaration that doesn’t hide an inherited member, a warning to that effect is issued. This warning is suppressed by removing the new modifier.

Access modifiers

A class-member-declaration can have any one of the five possible kinds of declared accessibility (§3.5.1): public, protected internal, protected, internal, or private. Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. When a class-member-declaration does not include any access modifiers, private is assumed.

Constituent types

Types that are used in the declaration of a member are called the constituent types of that member. Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or instance constructor. The constituent types of a member must be at least as accessible as that member itself (§3.5.4).

Static and instance members

Members of a class are either static members or instance members. Generally speaking, it is useful to think of static members as belonging to class types and instance members as belonging to objects (instances of class types).

When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member. Static members have the following characteristics:

· When a static member M is referenced in a member-access (§7.6.4) of the form E.M, E must denote a type containing M. It is a compile-time error for E to denote an instance.

· A static field identifies exactly one storage location to be shared by all instances of a given closed class type. No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.

· A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member.

When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

· When an instance member M is referenced in a member-access (§7.6.4) of the form E.M, E must denote an instance of a type containing M. It is a binding-time error for E to denote a type.

· Every instance of a class contains a separate set of all instance fields of the class.

· An instance function member (method, property, indexer, instance constructor, or destructor) operates on a given instance of the class, and this instance can be accessed as this (§7.6.7).

The following example illustrates the rules for accessing static and instance members:

class Test
{
int x;
static int y;

void F() {
x = 1; // Ok, same as this.x = 1
y = 1; // Ok, same as Test.y = 1
}

static void G() {
x = 1; // Error, cannot access this.x
y = 1; // Ok, same as Test.y = 1
}

static void Main() {
Test t = new Test();
t.x = 1; // Ok
t.y = 1; // Error, cannot access static member through instance
Test.x = 1; // Error, cannot access instance member through type
Test.y = 1; // Ok
}
}

The F method shows that in an instance function member, a simple-name (§7.6.2) can be used to access both instance members and static members. The G method shows that in a static function member, it is a compile-time error to access an instance member through a simple-name. The Main method shows that in a member-access (§7.6.4), instance members must be accessed through instances, and static members must be accessed through types.

Nested types

A type declared within a class or struct declaration is called a nested type. A type that is declared within a compilation unit or namespace is called a non-nested type.

In the example

using System;

class A
{
class B
{
static void F() {
Console.WriteLine("A.B.F");
}
}
}

class B is a nested type because it is declared within class A, and class A is a non-nested type because it is declared within a compilation unit.

Fully qualified name

The fully qualified name (§3.8.1) for a nested type is S.N where S is the fully qualified name of the type in which type N is declared.

Declared accessibility

Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct:

· A nested type that is declared in a class can have any of five forms of declared accessibility (public, protected internal, protected, internal, or private) and, like other class members, defaults to private declared accessibility.

· A nested type that is declared in a struct can have any of three forms of declared accessibility (public, internal, or private) and, like other struct members, defaults to private declared accessibility.

The example

public class List
{
// Private data structure
private class Node
{
public object Data;
public Node Next;

public Node(object data, Node next) {
this.Data = data;
this.Next = next;
}
}

private Node first = null;
private Node last = null;

// Public interface

public void AddToFront(object o) {...}

public void AddToBack(object o) {...}

public object RemoveFromFront() {...}

public object RemoveFromBack() {...}

public int Count { get {...} }
}

declares a private nested class Node.

Hiding

A nested type may hide (§3.7.1) a base member. The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. The example

using System;

class Base
{
public static void M() {
Console.WriteLine("Base.M");
}
}

class Derived: Base
{
new public class M
{
public static void F() {
Console.WriteLine("Derived.M.F");
}
}
}

class Test
{
static void Main() {
Derived.M.F();
}
}

shows a nested class M that hides the method M defined in Base.

This access

A nested type and its containing type do not have a special relationship with regard to this-access (§7.6.7). Specifically, this within a nested type cannot be used to refer to instance members of the containing type. In cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the this for the instance of the containing type as a constructor argument for the nested type. The following example

using System;

class C
{
int i = 123;

public void F() {
Nested n = new Nested(this);
n.G();
}

public class Nested
{
C this_c;

public Nested(C c) {
this_c = c;
}

public void G() {
Console.WriteLine(this_c.i);
}
}
}

class Test
{
static void Main() {
C c = new C();
c.F();
}
}

shows this technique. An instance of C creates an instance of Nested and passes its own this to Nested’s constructor in order to provide subsequent access to C’s instance members.



Поделиться:


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

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