7.3. The C# Value Type

The C# value type stores data. The assignment to a variable of a value type creates a copy of the assigned value. Following are some of the common features of C# value types:

  • Unlike the Java primitive, all boxed C# value types are derived from the System.Object class.

  • A value type cannot contain the null value.

  • Each value type has a default constructor that initializes the default value of the type.

  • A value type cannot inherit from another value type.

  • The memory allocated for value types is usually allocated on the stack; this contrasts with reference types, for which the memory is allocated on the heap.

  • You can create new C# value types. In Java, the primitives are a fixed set.

C# defines two key-value types: struct types and enum types.

7.3.1. The struct Type

C and C++ programmers will be pleased to know that unlike Java, which got rid of structs, C# has chosen to use them. A struct type offers a lightweight construct for representing simple concepts that do not require a class representation. A struct type is a value type that can contain constructors, constants, fields, methods, properties, indexers, operators, and nested types. The following rules are worth noting about a C# struct type:

  • Unlike C++, C# does not let you declare a class using the keyword struct. In C#, classes are semantically different from structs. A struct is a value type, whereas a class is a reference type.

  • A struct type can implement an interface.

  • A struct type comes with a default constructor, and therefore you should not provide one for it.

  • You should not initialize the instance fields of a struct type.

  • A struct is usually created using the new operator. Unlike classes, struct types can be instantiated without the new operator. If you do not use new, the fields will remain unassigned, and the object cannot be used until all the fields are initialized.

  • A struct type is more lightweight (in terms of memory consumption) than a class and so should be used to represent simple objects.

Listing 7.1 shows a simple struct type.

Listing 7.1. A Simple struct Type (C#)
using System;
public struct Dimension {
  public int length,width,height;

  public Dimension (int length, int width, int height) {
    this.length = length;
    this.width = width;
    this.height = height;
  }
  public override string ToString() {
      return "L ="+length+" W = "+width+" H="+height;
  }
  public int Volume() { return (length * width * height); }
}

class MainClass {
  public static void Main() {

    Dimension dim = new Dimension();
    Dimension dim1 = new Dimension (10,10, 10);
    Dimension dim2 = new Dimension (10,10,10);

    Console.WriteLine (dim);
    Console.WriteLine ("dim1.Equals(dim2) "+dim1.Equals(dim2));

    Dimension dim3;
    dim3.height = 10;
    dim3.width = 10;
    dim3.length = 10;
    Console.WriteLine (dim3.Volume());
  }
}

Here is the output of Listing 7.1:

L=0 W = 0 H=0
dim1.Equals(dim2) True
1000

Note that the == operator cannot be used for comparing two structs.

Listing 7.1 shows a custom struct type. The C# language itself uses several built-in structs to represent the simple value types (which you would recognize as primitives in Java). Section 7.4 discusses the built-in simple types.

Structs may seem similar to classes, but there are important differences that you should be aware of. First, classes are reference types, and structs are value types. By using structs, you can create objects that behave like the built-in types and also enjoy their benefits.

When you call the new operator on a class, it will be allocated on the heap. However, when you instantiate a struct, it gets created on the stack. This yields performance gains. For example, the use of a struct rather than a class for a Dimension can make a large difference in the number of memory allocations performed at runtime. The following program creates and initializes an array of 1,000 dimensions. With Dimension implemented as a class, 1,001 separate objects are instantiated—one for the array, and one each for the 1,000 elements.

class Dimension
{
  public int length, width, height;
  public Point(int length, int width, int height) {
    this.length = length;
    this.width = width;
    this.height = height;
  }
}

class Test
{
  static void Main() {
    Dimension[] dimensions = new Dimension [1000];
    for (int i = 0; i < 1000; i++)
      dimension[i] = new Dimension (i, i*i);
  }
}

If Dimension is instead implemented as a struct then only one object is instantiated: the one for the array. The Dimension instances are allocated in-line within the array.

Using structs instead of classes can also make an application run slower or take up more memory. Passing a struct instance as a value parameter creates a copy of the struct; and because structs are passed by value, you could be making copies of 1,000 structs every time you call a method that takes an array of structs.

Unless you need reference-type semantics, the system may more efficiently handle a class that is smaller than 16 bytes as a struct.

7.3.2. The enum Type

Again, C and C++ programmers will note that C#, unlike Java, has retained the concept of an enum. The enum keyword is used to declare an enumeration, a distinct type consisting of a set of named constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1. For example:

enum Directions {North, South, East, West };

In this enumeration, North is 0, South is 1, East is 2, and so forth. Enumerators can have initializers to override the default values. For example:

enum Directions {North = 2, South, East, West };

In this enumeration, the sequence of elements is forced to start from 2 instead of 0.

An explicit cast is needed to convert from the enum type to an integral type. For example, the following statement assigns the enumerator Sun to a variable of the type int using a cast to convert from enum to int:

int x = (int) Directions.South;

Listing 7.2 shows a simple enum example.

Listing 7.2. An enum Example (C#)
using System;
public class EnumTest {
  enum Directions {North, South, West, East};
  enum Toxicity :long {Max = 1345456L, Min = 346664L};
  public static void Main() {
    int a = (int) Directions.North;
    int b = (int) Directions.South;
    Console.WriteLine("North= {0}", a);
    Console.WriteLine("South = {0}", b);

    //Explicit cast
    long x = (long) Toxicity.Max;
    long y = (long) Toxicity.Min;
    Console.WriteLine("Max = {0}", x);
    Console.WriteLine("Min = {0}", y);
  }
}

Listing 7.2 shows an enumeration Toxicity whose base type is a long. In both cases—when the enum uses the default int base type and when it uses the custom base type—an explicit cast is required when the enum assigns a value to a variable.

Here is the output of Listing 7.2:

North = 0
South = 1
Max = 1345456
Min = 346664

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

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