Structures


As you know, classes are reference types. This means that class objects are accessed through a reference. This differs from the value types, which are accessed directly. However, sometimes it would be useful to be able to access an object directly, in the way that value types are. One reason for this is efficiency. Accessing class objects through a reference adds overhead onto every access. It also consumes space. For very small objects, this extra space might be significant. To address these concerns, C# offers the structure. A structure is similar to a class, but is a value type, rather than a reference type.

Structures are declared using the keyword struct and are syntactically similar to classes. Here is the general form of a struct:

 struct name : interfaces {   // member declarations }

The name of the structure is specified by name.

Structures cannot inherit other structures or classes, or be used as a base for other structures or classes. (Of course, like all C# types, structures do inherit object.) However, a structure can implement one or more interfaces. These are specified after the structure name using a comma-separated list. Like classes, structure members include methods, fields, indexers, properties, operator methods, and events. Structures can also define constructors, but not destructors. However, you cannot define a default (parameterless) constructor for a structure. The reason for this is that a default constructor is automatically defined for all structures, and this default constructor can’t be changed. The default constructor initializes the fields of a structure to their default values. Since structures do not support inheritance, structure members cannot be specified as abstract, virtual, or protected.

A structure object can be created using new in the same way as a class object, but it is not required. When new is used, the specified constructor is called. When new is not used, the object is still created, but it is not initialized. Thus, you will need to perform any initialization manually.

Here is an example that uses a structure to hold information about a book:

 // Demonstrate a structure. using System; // Define a structure. struct Book {   public string author;   public string title;   public int copyright;   public Book(string a, string t, int c) {     author = a;     title = t;     copyright = c;   } } // Demonstrate Book structure. class StructDemo {   public static void Main() {     Book book1 = new Book("Herb Schildt",                           "C#: The Complete Reference",                           2005); // explicit constructor     Book book2 = new Book(); // default constructor     Book book3; // no constructor     Console.WriteLine(book1.title + " by " + book1.author +                       ", (c) " + book1.copyright);     Console.WriteLine();     if(book2.title == null)       Console.WriteLine("book2.title is null.");     // now, give book2 some info     book2.title = "Brave New World";     book2.author = "Aldous Huxley";     book2.copyright = 1932;     Console.Write("book2 now contains: ");     Console.WriteLine(book2.title + " by " + book2.author +                       ", (c) " + book2.copyright);     Console.WriteLine(); // Console.WriteLine(book3.title); // error, must initialize first     book3.title = "Red Storm Rising";     Console.WriteLine(book3.title); // now OK   } }

The output from this program is shown here:

 C#: The Complete Reference by Herb Schildt, (c) 2005 book2.title is null. book2 now contains: Brave New World by Aldous Huxley, (c) 1932 Red Storm Rising

As the program shows, a structure can be initialized either by using new to invoke a constructor, or by simply declaring an object. If new is used, then the fields of the structure will be initialized, either by the default constructor, which initializes all fields to their default values, or by a user-defined constructor. If new is not used, as is the case with book3, then the object is not initialized, and its fields must be set prior to using the object.

When you assign one structure to another, a copy of the object is made. This is an important way in which struct differs from class. As explained earlier in this book, when you assign one class reference to another, you are simply changing the object to which the reference on the left side of the assignment is referring. When you assign one struct variable to another, you are making a copy of the object on the right. For example, consider the following program:

 // Copy a struct. using System; // Define a structure. struct MyStruct {   public int x; } // Demonstrate structure assignment. class StructAssignment {   public static void Main() {     MyStruct a;     MyStruct b;     a.x = 10;     b.x = 20;     Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);     a = b;     b.x = 30;     Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);   } }

The output is shown here:

 a.x 10, b.x 20 a.x 20, b.x 30

As the output shows, after the assignment

 a = b;

the structure variables a and b are still separate and distinct. That is, a does not refer to or relate to b in any way other than containing a copy of b’s value. This would not be the case if a and b were class references. For example, here is the class version of the preceding program:

 // Copy a class. using System; // Define a structure. class MyClass {   public int x; } // Now show a class object assignment. class ClassAssignment {   public static void Main() {     MyClass a = new MyClass();     MyClass b = new MyClass();     a.x = 10;     b.x = 20;     Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);     a = b;       b.x = 30;      Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);   } }

The output from this version is shown here:

 a.x 10, b.x 20 a.x 30, b.x 30

As you can see, after the assignment of b to a, both variables refer to same object—the one originally referred to by b.

Why Structures?

At this point you might be wondering why C# includes the struct since it seems to be a less-capable version of a class. The answer lies in efficiency and performance. Because structures are value types, they are operated on directly rather than through a reference. Thus, a struct does not require a separate reference variable. This means that less memory is used in some cases. Furthermore, because a struct is accessed directly, it does not suffer from the performance loss that is inherent in accessing a class object. Because classes are reference types, all access to class objects is through a reference. This indirection adds overhead to every access. Structures do not incur this overhead. In general, if you need to simply store a group of related data, but don’t need inheritance and don’t need to operate on that data through a reference, then a struct can be a more efficient choice.

Here is another example that shows how a structure might be used in practice. It simulates an e-commerce transaction record. Each transaction includes a packet header that contains the packet number and the length of the packet. This is followed by the account number and the amount of the transaction. Because the packet header is a self-contained unit of information, it is organized as a structure. This structure can then be used to create a transaction record, or any other type of information packet.

 // Structures are good when grouping data. using System; // Define a packet structure. struct PacketHeader {   public uint packNum; // packet number   public ushort packLen; // length of packet } // Use PacketHeader to create an e-commerce transaction record. class Transaction {   static uint transacNum = 0;   PacketHeader ph;  // incorporate PacketHeader into Transaction   string accountNum;   double amount;   public Transaction(string acc, double val) {    // create packet header     ph.packNum = transacNum++;     ph.packLen = 512;  // arbitrary length     accountNum = acc;     amount = val;   }   // Simulate a transaction.   public void sendTransaction() {     Console.WriteLine("Packet #: " + ph.packNum +                       ", Length: " + ph.packLen +                       ",\n    Account #: " + accountNum +                       ", Amount: {0:C}\n", amount);   } } // Demonstrate Packet class PacketDemo {   public static void Main() {     Transaction t = new Transaction("31243", -100.12);     Transaction t2 = new Transaction("AB4655", 345.25);     Transaction t3 = new Transaction("8475-09", 9800.00);     t.sendTransaction();     t2.sendTransaction();     t3.sendTransaction();   } }

The output from the program is shown here:

 Packet #: 0, Length: 512,     Account #: 31243, Amount: ($100.12) Packet #: 1, Length: 512,     Account #: AB4655, Amount: $345.25 Packet #: 2, Length: 512,     Account #: 8475-09, Amount: $9,800.00

PacketHeader is a good choice for a struct because it contains only a small amount of data and does not use inheritance, or even contain methods. As a structure, PacketHeader does not incur the additional overhead of a reference, as would a class. Thus, any type of transaction record can use PacketHeader without affecting its efficiency.

As a point of interest, C++ also has structures and uses the struct keyword. However, C# and C++ structures are not the same. In C++, struct defines a class type. Thus, in C++, struct and class are nearly equivalent. (The difference has to do with the default access of their members, which is private for class and public for struct.) In C#, a struct defines a value type, and a class defines a reference type.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

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