Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# ПІДРУЧНИКИ / c# / Manning - Windows.forms.programming.with.c#.pdf
Скачиваний:
108
Добавлен:
12.02.2016
Размер:
14.98 Mб
Скачать

The <name> for a namespace can be a single identifier, or a series of identifiers separated by periods. Nested namespaces are declared in the same way as non-nested namespaces. The various kinds of type declarations each have their own syntax, and are described next.

A.2 TYPES

All types are classified as either a value type or a reference type. These correspond to whether the type stores the actual data, or value, for the type, or whether the type simply stores a reference to the actual data.

Value types include simple built-in types such as int and char, enumerations, and structures. A value type contains its data. For example, an int type assigned to the number 5 stores this number directly. Thus, two different value types contain separate copies of the data and, therefore, modifying one of these types has no affect on the other. Value types include the built-in types, structures, and enumerations.

Reference types, on the other hand, contain a reference to their data. Examples include the string type and all Windows Forms controls. A string type assigned to the string “Hello” stores a reference to a section of memory where the characters “Hello” are actually stored. The area of memory reserved for reference types is called the heap, and is managed internally by the .NET Framework. Thus, two different reference types can point to the same physical data. As a result, the modification of one reference type can affect another reference type. Reference types include classes, interfaces, delegates, and arrays.

The following table illustrates the difference between these two kinds of types.

Comparison of value and reference types

 

Value type

 

Reference type

 

 

 

Declaration

struct ValInt {

class RefInt {

public

int vData;

public int rData;

 

}

 

}

 

 

 

 

ValInt v1, v2;

RefInt r1, r2;

Usage

v1.vData

= 5;

r1.rData = 5;

v2 = v1;

 

r2 = r1;

 

 

 

v1.vData

= 7

r1.rData = 7;

 

 

 

Result

Value of v2.vData is still 5.

Value of r2.rData is now 7.

 

 

 

 

In the value type column of the above table, the assignment of v2 = v1 copies the contents of v1 into v2. As a result, changing the value of v1.vData has no effect on the value stored by v2. In the reference column, the assignment of r2 = r1 causes both objects to refer to the same data. Here, changing the value of r1.rData also affects the value seen by r2. Note that all value types in the .NET Framework implicitly inherit from the System.ValueType class. This class overrides the methods

TYPES

639

System.Object class with more appropriate implementations

inherited from the for value types.

Back to the topic at hand, a type is specified with a type declaration as part of a namespace, or within the default namespace. The possible type declarations are classes, structures, interfaces, enumerations, and delegates.

A.2.1 CLASSES

A class is a reference type that defines a new data abstraction. Each class is composed of one or more members that define the contents, operations, and behavior permitted by instances of the class.

A class is declared using the class keyword in the following manner:

<modifiers>opt class <identifier> : <base>opt <interfaces>opt

{

<class-members>

}

where

<modifiers> is optional, and is an accessibility level as defined in the subsequent table or one of the keywords new, abstract, or sealed. If unspeci-

fied, a class is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<identifier> is the unique name to assign to the class.

<base> is optional, and defines a single base class for the new class.

<interfaces> is optional, and specifies one or more interface types which this class supports. If both <base> and <interfaces> are omitted, then the

colon ‘:’ is also omitted.

<class-members> are the members of the class. The possible members of a class are constants, fields, methods, properties, events, indexers, operators, constructors, and nested type declarations. Nested type declarations are simply other types defined to exist within the declarative scope defined by the class. The other kinds of members are discussed in the subsequent sections.

Every member of a class, and in fact every member of any type, has a defined accessibility associated with it. The accessibility of a member controls which regions of a program may make use of that member. The five levels of accessibility are shown in the following table:

Accessibility levels for C# types

Accessibility level

Meaning

 

 

public

Any type in any assembly can access the member.

protected

Any derived type in any assembly can access the member.

 

 

640

APPENDIX A C# PRIMER

Accessibility levels for C# types (continued)

Accessibility level

Meaning

 

 

internal

Any type in the same assembly can access the member.

protected internal

Any derived type in the same assembly can access the member.

private

Only the containing type can access the member.

 

 

These accessibility levels are used to declare nested types as well as other members. The default accessibility level of top-level types is internal. Within a class declaration, the default accessibility level is private. The default value of a class instance is null.

The various kinds of class members other than nested types are described in the following sections.

Constants

A constant is an unchangeable value that can be computed at compile time. A constant is declared using the const keyword in the following manner:

<modifiers>opt const <type> <constant-name> = <value> ;

where

<modifiers> is optional, and must be either an accessibility level or the new keyword. If unspecified, a constant is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<type> is any value type.

<constant-name> is the unique name for the constant.

<value> is the fixed value to assign to the constant.

A few examples of constant declarations are given below.

const int DaysPerYear = 365;

//The constant value here is calculated by the compiler. const double AlmostPi = 22.0 / 7.0;

//A constant taken from a public enumeration.

public enum Weekday = { Sun, Mon, Tue, Wed, Thu, Fri, Sat }; protected const Weekday FirstDayOfWeek = Sun;

Fields

A field is a variable value that can be modified at run time. A field is declared in the following manner:

<modifiers>opt <type> <field-name> = <initial-value> ;

where

TYPES

641

<modifiers> is optional, and must be either an accessibility level or one of the keywords new, readonly, static, or volatile. If unspecified, a field is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<type> is any valid type.

<field-name> is the unique name for the field.

<initial-value> is the value to initially assign to the field. This value may

be modified by the program at runtime.

A few examples of field declarations are given below.

public readonly string _defaultDir = @"C:\My Documents\Albums"; private PhotoAlbum _album;

// Possible fields in a Fraction class public class Fraction

{

private long _num; private long _den;

. . .

}

Methods

A method is a member that implements an operation or action that can be performed by a class or other object. For example, in a Fraction class, a method could be used to add two fractions together or compute the inverse of a fraction. A method may return a result, and can optionally accept one or more parameters that are used to perform the implemented action. A method is declared in the following manner:

<modifiers>opt <return-type> <member-name> ( <parameters>opt )

{

<statements>opt

}

where

<modifiers> is optional, and must be either an accessibility level or one of the keywords new, static, virtual, sealed, override, abstract, or extern. If unspecified, a method is assigned the default accessibility level of

the containing declarative scope. Multiple complementary modifiers may be specified.

<return-type> is either a valid type or the void keyword. When a type is specified, the return keyword is used to return an instance of this type as the result of the method.

<member-name> is the unique name for the method.

642

APPENDIX A C# PRIMER

<parameters> is optional. When specified, each parameter provides a type and an identifier, with possible modifiers out and ref. The params keyword may be used as the final parameter to indicate an array of values of a given type.

<statements> is optional and specifies one or more statements specifying the computer instructions for performing the defined action.

A few examples of method declarations that might be provided as part of a Fraction class are given as follows:

// public method

public void Add(Fraction b)

{

this._den = this._den * b._den;

this._num = (this._num * b._den) + (b._num * this._den);

}

//protected method with ref parameter protected void Invert(ref Fraction a)

{

Fraction f = new Fraction(a._den, a._num); a = f;

}

//static method with return type and params parameter

public static Fraction AddMultiple(params Fractions[] fracts)

{

Fraction a = new Fraction(1, 1); foreach (Fraction f in fracts)

{

a.Add(f);

}

return a;

}

Properties

A property is a member that provides access to a characteristic of a class or other object. For example, in a Fraction class, a property might provide the numerator of the fraction, or the floating-point value of the fraction. A property provides accessors that specify the operations to perform when its value is read or written. A property may support both read and write accessors, called get and set respectively, or be read-only or write-only. A property is declared in the following manner:

<modifiers>opt <type> <member-name>

{

<property-accessors>

}

where

TYPES

643

<modifiers> is optional, and must be either an accessibility level or one of the keywords new, static, virtual, sealed, override, abstract, or extern. If unspecified, a property is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<type> is the type for the property.

<member-name> is the unique name for the property.

<property-accessors> is one or both of the get and set accessor. Each

accessor consists of its accessor type, either get or set, and the block of statements defining the programming instructions for this accessor. In the get accessor, the type of the property must be returned using the return keyword. In the set accessor, an implicit parameter called value is used to represent the instance of the specified type provided by the caller.

Note that properties are declared much like methods, except that properties do not use parentheses and cannot have explicit parameters. A few examples of property declarations that might be used within a Fraction class are given below.

public long Numerator

{

get { return this._num; } set { this._num = value; }

}

public long Denominator

{

get { return this._den; } set

{

if (value == 0)

throw new DivideByZeroException("Denominator cannot be zero"); this._den = value;

}

}

// a read-only property protected double Value

{

get { return ((double)this._num / (double)this._den); }

}

Events

An event is a member that enables a class or other object to provide notifications. An instance of a class can associate one or more methods, known as event handlers, with specific events in order to receive such notifications. An event is declared using the event keyword. Like properties, an event can declare accessors to specify how event handlers are added to or removed from the event. Such accessors are optional, resulting in the following forms for an event declaration:

644

APPENDIX A C# PRIMER

<modifiers>opt event <delegate-type> <member-name> ;

<modifiers>opt event <delegate-type> <member-name>

{

<event-accessors>

}

where

<modifiers> is optional, and must be an accessibility level or one of the keywords new, static, virtual, sealed, override, abstract, or extern.

If unspecified, a property is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<delegate-type> is the delegate on which this event is based.

<member-name> is the unique name for the property.

<event-accessors>, when specified, must provide both the add and remove accessor. These accessors define how a method is added to and

removed from the event. In both accessors, an implicit parameter called value is used to represent the specified method.

Outside of the type where an event is defined, only the += and –= operators are permitted in order to add and remove methods, respectively. Methods are added to events as delegate instances based on the delegate type for the event. The following code shows how a DivideByZero event might be implemented within a Fraction class:

// public class for event data public class DivideByZeroArgs

{

. . .

}

public delegate void DivideByZeroHandler(object sender, DivideByZeroArgs e);

public class Fraction

{

. . .

//Declare the DivideByZero event for this class public event DivideByZeroHandler DivideByZero;

//Declare a method to invoke the event

public virtual void OnDivideByZero(DivideByZeroArgs e)

{

if (DivideByZero == null)

{

// No handlers, so raise exception

throw new DivideByZeroException("Divide by zero");

}

else

DivideByZero(this, e); // call event handlers

}

TYPES

645

// Declare property that can invoke event public long Denominator

{

get { return this._den; } set

{

if (value == 0)

{

DivideByZeroArgs args = new DivideByZeroArgs(..);

OnDivideByZero(this, args);

// Do something based on event handler

}

else

this._den = value;

}

}

Indexers

An indexer is a member that enables an object to be treated as an array. Elements in the “array” are referenced using square brackets. An indexer employs the this keyword as part of its declaration, which typically appears as follows:

<modifiers>opt <type> this [ <parameters> ]

{

<accessors>

}

where

<modifiers> is optional, and must be an accessibility level or one of the keywords new, virtual, sealed, override, or abstract. If unspecified, an indexer is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<type> is the type returned by this indexer. This typically corresponds to the type of objects contained by the containing class.

<parameters> are the parameters for the indexer. The format corresponds to that of a method, except that at least one parameter is required for an indexer, and ref and out parameters are not permitted.

<property-accessors> provide the block of statements associated with reading and writing indexer elements. These are identical to the accessors used for properties.

The following code shows a PartsOfOne class that provides the fractions between 0 and 1, inclusive, that divide an object into an equal number of parts. An indexer is used to return the nth Fraction object. For example, PartsOfOne(3) will return the fractions for zero (as 0 over 3), one-third, two-thirds, and one (as 3 over 3).

public class PartsOfOne

{

646

APPENDIX A C# PRIMER

private ulong _parts;

PartsOfOne(ulong parts)

{

_parts = parts;

}

// Indexer to return nth part as a Fraction between 0 and 1 public Fraction this[ulong n]

{

if (n < 0 || n > _parts)

throw new IndexOutOfRangeException();

return new Fraction(n, _parts);

}

}

Operators

An operator is a member that defines the meaning of an expression operator as applied to an instance of an object. There are three types of operators. A unary operator applies to a single type, a binary operator applies to two types, and a conversion operator converts an object from one type to another. The corresponding three operator types all use the operator keyword, and are formatted as follows:

<modifiers> <type> operator <unary-op> (<parameter>)

{

<statements>

}

<modifiers> <type> operator <binary-op> (<parameter>, <parameter>)

{

<statements>

}

<modifiers> <conv-kind> operator <type> (<parameter>)

{

<statements>

}

where

<modifiers> must be one of the keywords public, static, or extern.

<type> is the type returned by the operator.

<unary-op> is a unary operator: + - ! ~ ++ -- true false

<binary-op> is a binary operator: + - * / % & | ^ << >> == != > < >= <=

<conv-kind> is the kind of conversion, either implicit or explicit. An implicit conversion is invoked automatically by the compiler, such as from int to long. An explicit conversion requires an explicit cast, such as from int to byte.

TYPES

647

<parameter> is a type and identifier to accept in the conversion.

<statements> is the block of statements associated with the operator. This

block must return a value of the specified type.

The following code shows an example of unary, binary, and conversion operator declaration for a Fraction class.

//Unary operator for the negative operation public Fraction operator -(Fraction a)

{

return new Fraction(-a.Numerator, a.Denominator);

}

//Binary operator for the addition operation public Fraction operator +(Fraction a, Fraction b)

{

int den = a.Denominator * b.Denominator; int num = (a.Numerator * b.Denominator)

+ (b.Numerator * a.Denominator); return new Fraction(num, den);

}

// Explicit conversion from Fraction to double static explicit operator double(Fraction a)

{

return ((double)a.Numerator / (double)a.Denominator);

}

Constructor

A constructor is a member that initializes a class or an instance of a class or other object. There are two types of constructors. A static constructor performs one-time initialization for an object, while an instance constructor initializes a specific instance of an object. Static constructors cannot be invoked explicitly and are executed at most once in a program after any static fields have been initialized and before any static class members are referenced or instances of the class created. Instance constructors are executed as an object is created. The default constructor for a class is an instance constructor with no parameters, and is created automatically if no instance constructors for a class are provided.

Constructors are declared as follows, with static constructors declared using the static keyword:

static <identifier>()

{

<statements>

}

<modifiers> <identifier> (<parameters>opt) <initializer>opt

{

<statements>

}

648

APPENDIX A C# PRIMER

where

<identifier> is the name of the type for which the constructor is defined.

<modifiers> is optional, and must be an accessibility level or the keyword

extern. If unspecified, a constructor is assigned the default accessibility level of the containing declarative scope. Multiple complementary modifiers may be specified.

<parameters> is optional, and specifies one or more parameters for the constructor. These are identical to method parameters.

<initializer> is optional, and specifies another instance constructor to invoke before this instance constructor is executed. This has the form base(<args>) or this(<args>), where <args> specifics zero or more arguments for the constructor to invoke. The base keyword form invokes an instance constructor in the base class, while the this keyword form invokes another instance constructor in the same object.

<statements> is the block of statements associated with the constructor.

The following code shows some examples of constructors as might be provided for a

Fraction class:

public class Fraction

{

private static readonly int Unit;

// This a lame example of a static constructor static Fraction()

{

Unit = 1;

}

private long _num; private long _den;

// Instance constructors

public Fraction(long top, long bottom)

{

_num = top; _den = bottom;

}

public Fraction(long number) : this(number, 1)

{

}

. . .

}

Destructor

A destructor is a member that implements the actions required to destroy an instance of a class. The destructor for a class may be invoked any time after the instance is no

TYPES

649

Соседние файлы в папке c#