C# Tutorial Lesson 11: Introducing Classes, Structs and Namespaces

Classes and Types

As we noted previously, one can create new reference types by defining classes. Classes provide 'templates' from which these direct instances are generated. Where we appeal to the relation between a class and its corresponding reference type instances we shall say that a class specifies the type (also that the class specifies the constitutive elements of the type).

Any type is made up of elements, which we term type members. There are two main kinds of type members that a class can specify. Firstly, a class can specify other types - both value and reference (for the distinction see lesson 4). This idea, that types can contain other types, is known within the literature on object orientation as 'containment', or else 'aggregation'. Where a type contains another reference type, we shall call it the containing type of the latter.

The second, main kind of type members that a class can specify are methods, functions designed for reading and manipulating the value and reference types an instance contains.

a picture illustrating a reference type object

Inheritance

Object oriented languages like C# allow inheritance from reference types. If a type inherits from another, it takes on all of its type members. A type can, however, both add to the members it inherits in this way, as well as 'overwriting' them. To overwrite a type member - a method, say - the defining class specifies a method with the same name as one that it inherits (this is covered in lesson 14).

C#'s inheritance model is more similar to Java's than to C++'s. In particular, C# classes inherit always from a single base class (if one is not specified in the declaration, inheritance is from System.Object). At the same time, however, C# classes can inherit from any number of interfaces.

Abstract Classes and Interfaces

Some classes are not designed to have direct instances. Rather, they are designed simply to be inherited from, by ancestors which may themselves have direct instances (or not). A class is 'abstract' just in case it cannot itself have direct instances.

Classes can be abstract because in a class it is possible to specify a class method without specifying its body. Such methods are themselves termed 'abstract'. Where a class contains an abstract method it cannot be instantiated, since it is not specified what should happen were the method to be called.

An 'interface' is a class which has only abstract methods. However, such a class is declared not with the 'class' keyword but the 'interface' keyword. Above we stated that a C# class can only inherit from one 'base' class; but this ignores interfaces. A class can inherit from any number of interfaces.

Nested Classes

Classes are usually specified independently of each other. But it is possible for one class to be specified within another's specification. In this case, the latter class is termed a nested class.

Structs

A struct is a user-defined value type. It is declared in a very similar way to a class, except that it can't inherit from any class, nor can any class inherit from it (as mentioned previously, however, all value types do inherit from System.object). The following example shows a partial declaration for a 'Coordinate' struct:

1.

struct Coordinate

2.

{

3.

    public int x;

4.

    public int y;

5.

6.

    public Coordinate(int x, int y)

7.

    {

8.

        this.x = x;

9.

        this.y = y;

10.

    }

11.

}


Given the above, one could initialise a Coordinate type in a familiar way, using code like:

Coordinate c = new Coordinate(10, 2);

Note that if a variable of a struct type is declared without being given an explicit value, eg:

Coordinate c2 ;

it does not equate to 'null' (this being the default value for reference types, rather than value types). Instead, the variable is initialised to a state where its fields have their default values. If these fields are basic value types, they will generally be set to zero. If these fields are reference types, they will be set to 'null'.

Because of this default initialisation behaviour, it is an error for a struct to be given a parameterless constructor (eg. one like 'public Coordinate()'). Also, where a struct does have a constructor, you should be sure to make assignments to all of the struct's fields within this constructor.

Namespaces

Namespaces can be thought of as collections of classes; they provide unique identifiers for types by placing them in an hierarchical structure.

To illustrate the use of namespaces: suppose that two different C# developers come up with a class called 'bank', one relating to fiscal institutions and the other relating to riversides. In a programming environment containing both classes, there is a need to distinguish one from the other, and this is achieved by placing them within different namespaces. For example, the former class could be placed within the 'fiscal' namespace, say, becoming fiscal.bank, whereas the latter could be placed within the 'river' namespace becoming river.bank. (Note that C# does not include Java's direct link between the namespace hierarchy and the file structure hierarchy).

Most classes depend upon the existence of other classes - for instance, they may specify contained types. It is possible in the specification always to write each class' full namespace, but these are often too long for it to be worthwhile. To take an example at random, the following is the fully qualified name of a class in the .NET framework relating to a particular type of cryptographic algorithm:

System.Security.Cryptography.AsymmetricAlgorithm

This problem is addressed by the use of the 'using' keyword, placed at the very top of the class specification. For instance, in a class specification including the phrase

using System.Security.Cryptography;

one could write refer to the above class simply using its class name

AsymmetricAlgorithm

Alternatively, one could specify an alias for the namespace, eg

using myAlias = System.Security.Cryptography;

and then refer to the class with

myAlias.AsymmetricAlgorithm

One specifies a namespace for one's own classes using the 'namespace' keyword. For instance, the following code states that the class 'Adder' is in the namespace fred.math.

1.

namespace fred

2.

{

3.

    namespace math

4.

    {

5.

        public class Adder

6.

        {

7.

            // insert code here

8.

        }

9.

    }

10.


Alternatively, and more simply, one write the above as:

1.

namespace fred.math

2.

{

3.

    public class Adder

4.

    {

5.

        // insert code here

6.

    }

7.


Link Building Information