26.2 Differences between a struct and a class


Despite their apparent similarity, there are several differences between a struct and a class “ and you cannot use structs as if they are identical to classes. Five major differences are discussed below.

26.2.1 Value type versus reference type

The most important distinction is that a class is a reference type, while a struct is a value type. [6]

[6] A brief recap of reference and value types follows for convenience. See Chapter 9 for a fuller explanation.

A reference type variable stores the address of an object. It is possible to have multiple reference type variables referring (or pointing) to the same object in memory, so that any operations on one of the variables will affect the same shared object.

A class is a reference type:

 1: using System;  2:  3:  class  Book {  4:   public string Title;  5:   public float Price;  6: }  7:  8: class TestClass{  9:   public static void Main(){ 10: 11:     Book b1 = new Book(); 12:     b1.Title = "Romeo and Juliet"; 13: 14:     Book b2 = b1; 15:     b2.Title = "Julius Caesar"; 16: 17:     Console.WriteLine(b1.Title); 18:   } 19: } 

Output:

 Julius Caesar 

On line 14, the address where the Book object is stored is assigned to b2 , so that both reference type variables, b1 and b2 , refer to the same Book object. The expressions b1.Title and b2.Title refer to the same Title field of the shared Book object. Throughout the whole program, only one Book object is created.

A value type variable, on the other hand, stores the actual value of the type instead of an address. It is, of course, possible for two value type variables to store the same value, but changing the value of one will not affect the other.

Changing line 3 of the code above from:

 3:  class  Book { 

to

 3:  struct  Book { 

will result in a totally different output:

 Romeo and Juliet 

What's the reason for the different output? In this case, Book is a struct instead of a class, and a struct is a value type. When assigning a value type variable to another value type variable, a copy of the value is created. Hence on line 14, when b1 is assigned to b2 , a copy of the Book struct is created and assigned to value type variable b2 . When b2 's Title field is changed to "Julius Caesar" , b1 's Title field is not affected ( b1.Title remains as "Romeo and Juliet" ).

It is important to understand that structs are value types and not reference types like classes because of this significant implication . This is shown in Figure 26.1.

Figure 26.1. A representation of how memory looks after the assignment on line 14 if Book is a class (left), and if Book is a struct (right).

graphics/26fig01.gif

26.2.2 Inheritance

Unlike classes, structs cannot be inherited from. A struct:

  • is automatically a direct subclass of System.ValueType . You cannot specify that a struct be a subclass of any class (even System.ValueType , since that is the default). ValueType is a direct subclass of System.Object .

  • is implicitly sealed [7] but cannot be declared with the sealed modifier. (Hence, you cannot inherit from a struct.)

    [7] A sealed class in C# is a final class in Java nomenclature . You cannot subclass a sealed class.

  • cannot contain abstract methods [8] and cannot be declared with the abstract modifier.

    [8] It makes complete sense that a sealed class cannot contain abstract methods. Hence if you view a struct as sealed, it too cannot contain abstract methods.

  • cannot contain members that are declared as protected or protected internal . [9]

    [9] A protected member does not make sense in a struct, simply because structs cannot be inherited from.

Nevertheless, a struct can implement interfaces. Since structs cannot have any abstract methods, all abstract methods in the interfaces implemented must be coded in the struct. The following program shows an example of a struct implementing an interface.

 1: using System;  2:  3: interface SolidShape{  4:   float CalculateSurfaceArea();  5:   float CalculateVolume();  6: }  7:  8: struct Cube  :SolidShape  {  9:   public float Side; 10: 11:   public float CalculateVolume(){ 12:     return Side*Side*Side; 13:   } 14: 15:   public float CalculateSurfaceArea(){ 16:     return 6*Side*Side; 17:   } 18: } 19: 20: class TestClass{ 21:   public static void Main(){ 22:     Cube c = new Cube(); 23:     c.Side = 2f; 24:     Console.WriteLine(c.CalculateSurfaceArea()); 25:   } 26: } 

Output:

 c:\expt>test 24 

26.2.3 Default constructor

A struct implicitly has a default constructor. [10] For classes, a default constructor is provided only when no constructor is explicitly coded. In the case of structs, the default constructor is always provided regardless of whether other overloaded constructors are coded or not.

[10] A default constructor is one which takes in no parameters. It is also known as a parameterless constructor.

What this default constructor does in a struct is set all the struct's field values to their default values ( null for reference types or for numeric types). Interestingly, you cannot override the implicit default constructor provided in a struct, though you can have other overloaded constructors which take in other parameters.

All a struct's fields must be initialized before the constructor completes. Study the following example.

 1: using System;  2:  3: struct Book {  4:   public string Title;  5:   public float Price;  6:  7:   public Book (string newTitle, float newPrice){  8:     Title = newTitle;  9:     Price = newPrice; 10:   } 11: } 12: 13: class TestClass{ 14:   public static void Main(string []args){ 15: 16:     Book b1 = new Book(); 17:     Console.WriteLine("Title is " + b1.Title); 18:     Console.WriteLine("Price is " + b1.Price); 19:   } 20: } 

Output:

 Title is Price is 0 

Lines 7 “ 10 code an overloaded constructor for the Book struct which takes in a string and a float . On line 16, a new struct is created. [11] In this case, the default constructor is invoked, [12] which initializes the fields Title and Price to null and respectively. When printed out (line 17 “ 18), the value of the Title and Price fields are null and respectively.

[11] Remember that unlike a class, a struct “ being a value type “ is created on the stack, rather than the heap. No new object is created although the new operator is used here.

[12] The default constructor is not coded but always provided free-of-charge even if you explicitly provide another constructor (you cannot override the default constructor in a struct anyway).

Changing line 16 from:

 16:     Book b1 = new Book(); 

to:

 16:     Book b1 = new Book("Macbeth",10.30f); 

will result in a different output:

 Title is Macbeth Price is 10.3 

This proves that the overloaded constructor which was written has executed.

However, consider the case if line 9 is removed so that the Price field is not initialized in the overloaded constructor which takes in a string and a float .

 7:   public Book (string newTitle, float newPrice){  8:     Title = newTitle;  9:     // Price = newPrice; 10:   } 

Compilation error:

[View full width]
 
[View full width]
Test.cs(7,10): error CS0171: Field 'Book.Price' must be fully assigned before control graphics/ccc.gif leaves the constructor

Compilation isn't successful because a constructor has been written which does not initialize all the struct's fields before returning. You must ensure that all fields of a struct are explicitly initialized before the constructor ends. For classes, all fields will be automatically initialized to their default values regardless of which constructor runs. For structs, field initialization must be explicitly performed by the constructor which runs.

Attempting to alter the overloaded constructor so that it calls the default constructor first (which initializes every field to default values) by using the base keyword does not work. The following change to line 7 results in a compilation error which explains why:

 7:   public Book (string NewTitle, float newPrice)  :base()  { 

Compilation error:

[View full width]
 
[View full width]
test.cs(7,10): error CS0522: 'Book.Book(string,float)': structs cannot call base class graphics/ccc.gif constructors

It seems that you have to initialize all your fields manually in every overloaded constructor you provide since you cannot invoke the default constructor to do that using the base keyword.

26.2.4 Using a struct without the new keyword

So far, you have used the new keyword to create a new struct value. Declaring a class type variable such as the following ( assuming MyClass is a class type):

 MyClass mc; 

does not actually create an object on the heap.

On the other hand, when a struct type variable is declared, the declaration actually allocates space for the whole struct on the stack. However, note that on variable declaration only, the struct's fields are not initialized and that implies that you cannot access any field's value yet. Study the example below.

 1: using System;  2:  3: struct Book {  4:   public string Title;  5:   public float Price;  6: }  7:  8: class MainClass{  9:   public static void Main(string []args){ 10: 11:  Book b1;  // Stack space allocated 12:     b1.Title = "Macbeth"; 13:     b1.Price = 7.35f; 14:     Console.WriteLine("Title is " + b1.Title); 15:   } 16: } 

Output:

 Title is Macbeth 

Note that the declaration statement on line 11 itself does not initialize the struct's fields. If you comment away lines 12 “ 13 but keep line 14, a compilation error occurs:

 11:     Book b1; 12:     // b1.Title = "Macbeth"; 13:     // b1.Price = 7.35f; 14:     Console.WriteLine("Title is " + b1.Title); 15:   } 16: } 

Compilation error:

 test.cs(14,44): error CS0170: Use of possibly unassigned field 'Title' 

You need to explicitly assign values to the fields you are going to access. When you assign a value to one field, other fields are not automatically initialized.

This works:

 11:     Book b1; 12:  b1.Title  =  "Macbeth";  13:     // b1.Price = 7.35f; 14:     Console.WriteLine("Title is " +  b1.Title  ); 15:   } 16: } 

but with a compilation warning:

[View full width]
 
[View full width]
Test.cs(5,16): warning CS0649: Field 'Book.Price' is never assigned to, and will always graphics/ccc.gif have its default value 0

Output:

 Title is Macbeth 

However, attempting to access Price without initializing it results in a compilation error:

 11:     Book b1; 12:     b1.Title = "Macbeth"; 13:     // b1.Price = 7.35f; 14:     Console.WriteLine("Price is " +  b1.Price  ); 15:   } 16: } 

Compilation error:

 test.cs(14,44): error CS0170: Use of possibly unassigned field 'Price' 

Just remember that a struct's field can only be accessed after it has been initialized “ and the default constructor initializes all fields to their default values. If you declare a struct without using the new keyword, the default constructor is not invoked and you will have to manually initialize each field before you can use it. So much for complex rules!

26.2.5 Field initializers

If Book is declared as a class like this:

 3:  class  Book{  4:   public string Title  =   "Othello"  ;  5:   public float Price  =   9.73f  ;  6: } 

the bold parts of lines 4 “ 5 are called the variable initializers or field initializers.

For classes, field initializers are legal. [13] Structs, however, do not allow field initializers. If line 3 is changed to:

[13] Some developers would argue that field initialization is an activity that should be performed in a constructor.

 3:  struct  Book{ 

The following compilation error appears:

 test.cs(4,17): error CS0573: 'Book.Title': cannot have instance field initializers in structs test.cs(5,16): error CS0573: 'Book.Price': cannot have instance field initializers in structs 

26.2.6 Summary of differences

Table 26.1 shows a summary of the points discussed above.

Table 26.1. Differences between struct and class

Feature

C# class

C# struct

Type

Reference

Value

Inheritance

Allowed

Not allowed (implicitly subclassed from System.ValueType ) “ can implement interfaces though

Declaration

Declaration itself does not create an object, or reserve memory space

Declaration reserves space on stack for whole struct even if the new keyword is not used

Default constructor

Provided implicitly only if no constructor is explicitly coded

Always provided implicitly and cannot be overridden “ default constructor initializes all fields to their default values

Fields

Instance (non-static) or class (static) fields are automatically assigned a default value if not explicitly assigned

If the default constructor is not invoked, you have to ensure that each field is manually assigned a value before accessing it

Field initializers

Allowed

Not allowed

Destructor

Allowed

Not allowed



From Java to C#. A Developers Guide
From Java to C#: A Developers Guide
ISBN: 0321136225
EAN: 2147483647
Year: 2003
Pages: 221
Authors: Heng Ngee Mok

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net