Classes

A class is the most common kind of reference type. The simplest possible class declaration is as follows:

class Foo
{
}

A more complex class optionally has the following:

Preceding the keyword class

Attributes and class modifiers. The non-nested class modifiers are public, internal, abstract, sealed, static, unsafe, and partial.

Following YourClassName

Generic type parameters, a base class, and interfaces

Within the braces

Class members (these are methods, properties, indexers, events, fields, constructors, overloaded operators, nested types, and a finalizer)

Fields

A field is a variable that is a member of a class or struct. For example:

class Octopus
{
  string name;
  public int Age = 10;
}

A field may have the readonly modifier to prevent it from being modified after construction. A read-only field can be assigned only in its declaration or within the enclosing type’s constructor.

Field initialization is optional. An uninitialized field has a default value (0, , null, false). Field initializers run before constructors, in the order in which they appear.

For convenience, you may declare multiple fields of the same type in a comma-separated list. This is a convenient way for all the fields to share the same attributes and field modifiers. For example:

static readonly int legs = 8, eyes = 2;

Methods

A method performs an action in a series of statements. A method can receive input data from the caller by specifying parameters and output data back to the caller by specifying a return type. A method can specify a void return type, indicating that it doesn’t return any value to its caller. A method can also output data back to the caller via ref and out parameters.

A method’s signature must be unique within the type. A method’s signature comprises its name and parameter types (but not the parameter names, nor the return type).

Overloading methods

A type may overload methods (have multiple methods with the same name), as long as the parameter types are different. For example, the following methods can all coexist in the same type:

void Foo (int x);
void Foo (double x);
void Foo (int x, float y);
void Foo (float x, int y);

Instance Constructors

Constructors run initialization code on a class or struct. A constructor is defined like a method, except that the method name and return type are reduced to the name of the enclosing type:

public class Panda
{
  string name;              // Define field
  public Panda (string n)   // Define constructor
  {
    name = n;               // Initialization code
  }
}
...
Panda p = new Panda ("Petey");   // Call constructor

A class or struct may overload constructors. One overload may call another, using the this keyword:

public class Wine
{
  public Wine (decimal price) {...}

  public Wine (decimal price, int year)
               : this (price) {...}
}

When one constructor calls another, the called constructor executes first.

You can pass an expression into another constructor as follows:

public Wine (decimal price, DateTime year)
             : this (price, year.Year) {...}

The expression itself cannot make use of the this reference, for example, to call an instance method. It can, however, call static methods.

Implicit parameterless constructors

For classes, the C# compiler automatically generates a parameterless public constructor if and only if you do not define any constructors. However, as soon as you define at least one constructor, the parameterless constructor is no longer automatically generated.

For structs, a parameterless constructor is intrinsic to the struct; therefore, you cannot define your own. The role of a struct’s implicit parameterless constructor is to initialize each field with default values.

Nonpublic constructors

Constructors do not need to be public. A common reason to have a nonpublic constructor is to control instance creation via a static method call. The static method could be used to return an object from a pool rather than creating a new object, or return a specialized subclass chosen based on input arguments.

Object Initializers

To simplify object initialization, the accessible fields or properties of an object can be initialized via an object initializer directly after construction. For example, consider the following class:

public class Bunny
{
  public string Name;
  public bool LikesCarrots, LikesHumans;

  public Bunny () {}
  public Bunny (string n) { Name = n; }
}

Using object initializers, you can instantiate Bunny objects as follows:

Bunny b1 = new Bunny {
                       Name="Bo",
                       LikesCarrots = true,
                       LikesHumans = false
                     };



Bunny b2 = new Bunny ("Bo") {
                              LikesCarrots = true,
                              LikesHumans = false
                            };

The this Reference

The this reference refers to the instance itself. In the following example, the Marry method uses this to set the partner’s mate field:

public class Panda
{
  public Panda Mate;

  public void Marry (Panda partner)
  {
    Mate = partner;
    partner.Mate = this;
  }
}

The this reference also disambiguates a local variable or parameter from a field. For example:

public class Test
{
  string name;
  public Test (string name) { this.name = name; }
}

The this reference is valid only within nonstatic members of a class or struct.

Properties

Properties look like fields from the outside, but internally they contain logic, like methods do. For example, you can’t tell by looking at the following code whether CurrentPrice is a field or a property:

Stock msft = new Stock();
msft.CurrentPrice = 30;
msft.CurrentPrice -= 3;
Console.WriteLine (msft.CurrentPrice);

A property is declared like a field, but with a get/set block added. Here’s how to implement CurrentPrice as a property:

public class Stock
{
  decimal currentPrice;  // The private "backing" field

  public decimal CurrentPrice    // The public property
  {
     get { return currentPrice; }
     set { currentPrice = value; }
  }
}

get and set denote property accessors. The get accessor runs when the property is read. It must return a value of the property’s type. The set accessor runs when the property is assigned. It has an implicit parameter named value of the property’s type that you typically assign to a private field (in this case, currentPrice).

Although properties are accessed in the same way as fields, they differ in that they give the implementer complete control over getting and setting its value. This control enables the implementer to choose whatever internal representation is needed, without exposing the internal details to the user of the property. In this example, the set method could throw an exception if value was outside a valid range of values.

Note

Throughout this book, we use public fields to keep the examples free of distraction. In a real application, you would typically favor public properties over public fields to promote encapsulation.

A property is read-only if it specifies only a get accessor, and it is write-only if it specifies only a set accessor. Write-only properties are rarely used. A property typically has a dedicated backing field to store the underlying data. However, it need not—it may instead return a value computed from other data.

Automatic properties

The most common implementation for a property is a getter and/or setter that simply reads and writes to a private field of the same type as the property. An automatic property declaration instructs the compiler to provide this implementation. We can redeclare the first example in this section as follows:

public class Stock
{
  public decimal CurrentPrice { get; set; }
}

The compiler automatically generates a private backing field of a compiler-generated name that cannot be referred to. The set accessor can be marked private if you want to expose the property as read-only to other types.

get and set accessibility

The get and set accessors can have different access levels. The typical use case for this is to have a public property with an internal or private access modifier on the setter:

private decimal x;
public decimal X
{
  get         { return x;  }
  private set { x = Math.Round (value, 2); }
}

Notice that you declare the property itself with the more permissive access level (public, in this case), and add the modifier to the accessor you want to be less accessible.

Indexers

Indexers provide a natural syntax for accessing elements in a class or struct that encapsulate a list or dictionary of values. Indexers are similar to properties, but are accessed via an index argument rather than a property name. The string class has an indexer that lets you access each of its char values via an int index:

string s = "hello";
Console.WriteLine (s[0]); // 'h'
Console.WriteLine (s[3]); // 'l'

The syntax for using indexers is like that for using arrays, except that the index argument(s) can be of any type(s).

Implementing an indexer

To write an indexer, define a property called this, specifying the arguments in square brackets. For instance:

class Sentence
{
  string[] words = "The quick brown fox".Split();

  public string this [int wordNum]      // indexer
  {
    get { return words [wordNum];  }
    set { words [wordNum] = value; }
  }
}

Here’s how we could use this indexer:

Sentence s = new Sentence();
Console.WriteLine (s[3]);       // fox
s[3] = "kangaroo";
Console.WriteLine (s[3]);       // kangaroo

A type may declare multiple indexers, each with parameters of different types. An indexer can also take more than one parameter:

public string this [int arg1, string arg2]
{
  get { ... }  set { ... }
}

If you omit the set accessor, an indexer becomes read-only.

Constants

A constant is a static field whose value can never change. A constant is evaluated statically at compile time and the compiler literally substitutes its value whenever used (rather like a macro in C++). A constant can be any of the built-in numeric types, bool, char, string, or an enum type.

A constant is declared with the const keyword and must be initialized with a value. For example:

public class Test
{
  public const string Message = "Hello World";
}

A constant is much more restrictive than a static readonly field—both in the types you can use and in field initialization semantics. A constant also differs from a static readonly field in that the evaluation of the constant occurs at compile time. Constants can also be declared local to a method:

static void Main()
{
  const double twoPI = 2 * System.Math.PI;
  ...
}

Static Constructors

A static constructor executes once per type, rather than once per instance. A type can define only one static constructor, and it must be parameterless and have the same name as the type:

class Test
{
  static Test() { Console.Write ("Type Initialized"); }
}

The runtime automatically invokes a static constructor just prior to the type being used. Two things trigger this: instantiating the type, and accessing a static member in the type.

Warning

If a static constructor throws an unhandled exception, that type becomes unusable for the life of the application.

Static field initializers run just before the static constructor is called. If a type has no static constructor, field initializers will execute just prior to the type being used—or anytime earlier at the whim of the runtime. (This means that the presence of a static constructor may cause field initializers to execute later in the program than they would otherwise.)

Static Classes

A class can be marked static, indicating that it must be composed solely of static members and cannot be subclassed. The System.Console and System.Math classes are good examples of static classes.

Finalizers

Finalizers are class-only methods that execute before the garbage collector reclaims the memory for an unreferenced object. The syntax for a finalizer is the name of the class prefixed with the ~ symbol:

class Class1
{
  ~Class1() { ... }
}

C# translates a finalizer into a method that overrides the Finalize method in the object class. We discuss garbage collection and finalizers fully in Chapter 12 of C# 5.0 in a Nutshell.

Partial Types and Methods

Partial types allow a type definition to be split—typically across multiple files. A common scenario is for a partial class to be auto-generated from some other source (e.g., a Visual Studio template), and for that class to be augmented with additional hand-authored methods. For example:

// PaymentFormGen.cs - auto-generated
partial class PaymentForm { ... }

// PaymentForm.cs - hand-authored
partial class PaymentForm { ... }

Each participant must have the partial declaration.

Participants cannot have conflicting members. A constructor with the same parameters, for instance, cannot be repeated. Partial types are resolved entirely by the compiler, which means that each participant must be available at compile time and must reside in the same assembly.

A base class may be specified on a single participant or on all participants. In addition, each participant can independently specify interfaces to implement. We cover base classes and interfaces in more detail in the sections Inheritance and Interfaces.

Partial methods

A partial type may contain partial methods. These let an auto-generated partial type provide customizable hooks for manual authoring. For example:

partial class PaymentForm    // In auto-generated file
{
  partial void ValidatePayment (decimal amount);
}

partial class PaymentForm    // In hand-authored file
{
  partial void ValidatePayment (decimal amount)
  {
    if (amount > 100) Console.Write ("Expensive!");
  }
}

A partial method consists of two parts: a definition and an implementation. The definition is typically written by a code generator, and the implementation is typically manually authored. If an implementation is not provided, the definition of the partial method is compiled away (as is the code that calls it). This allows auto-generated code to be liberal in providing hooks, without having to worry about bloat. Partial methods must be void and are implicitly private.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.216.75