In some situations all you really
need is a small data structure. In this case, a class provides more
functionality than you need, and for performance reasons you will probably
prefer to use a struct. Look at this example:
class Dimensions
{
public double Length;
public double Width;
}
This
code defines a class called Dimensions, which simply stores the length and
width of some item. Perhaps you're writing a furniture-arranging program to let
people experiment with rearranging their furniture on the computer and you want
to store the dimensions of each item of furniture. It looks like you're
breaking the rules of good program design by making the fields public, but the
point is that you don't really need all the facilities of a class for this at
all. All you have is two numbers, which you'll find convenient to treat as a
pair rather than individually. There is no need for lots of methods, or for you
to be able to inherit from the class, and you certainly don't want to have the
.NET runtime go to the trouble of bringing in the heap with all the performance
implications, just to store two doubles.
As
mentioned earlier in this chapter, the only thing you need to change in the
code to define a type as a struct instead of a class is to replace the keyword
class with struct:
struct Dimensions
{
public double Length;
public double Width;
}
Defining
functions for structs is also exactly the same as defining them for classes.
The following code demonstrates a constructor and a property for a struct:
struct Dimensions
{
public double Length;
public double Width;
Dimensions(double length, double width)
{
Length=length;
Width=width;
}
}
In
many ways, you can think of structs in C# as being like scaled-down classes.
They are basically the same as classes, but designed more for cases where you
simply want to group some data together. They differ from classes in the
following ways:
- Structs are value types, not
reference types. This means they are stored either in the stack or inline
(if they are part of another object that is stored on the heap) and have
the same lifetime restrictions as the simple data types.
- Structs do not support
inheritance.
- There are some differences in
the way constructors work for structs. In particular, the compiler always
supplies a default no-parameter constructor, which you are not permitted
to replace.
- With a struct, you can specify
how the fields are to be laid out in
Because
structs are really intended to group data items together, you'll sometimes find
that most or all of their fields are declared as public. This is, strictly
speaking, contrary to the guidelines for writing .NET code — according to
Microsoft, fields (other than const fields) should always be private and
wrapped by public properties. However, for simple structs, many developers
would nevertheless consider public fields to be acceptable programming
practice.
Note
|
C++
developers beware; structs in C# are very different from classes in their
implementation. This is very different than the situation in C++, for which
classes and structs are virtually the same thing.
|
The
following sections look at some of these differences in more detail.
Although structs are value types,
you can often treat them syntactically in the same way as classes. For example,
with the definition of the Dimensions class in the previous section, you
could write:
Dimensions point = new Dimensions();
point.Length = 3;
Note
that because structs are value types, the new operator does not work in the
same way as it does for classes and other reference types. Instead of
allocating memory on the heap, the new operator simply calls the appropriate
constructor, according to the parameters passed to it, initializing all fields.
Indeed, for structs it is perfectly legal to write:
Dimensions point;
point.Length = 3;
point.Width = 6;
If
Dimensions was a class, this would produce a compilation error, because point
would contain an uninitialized reference — an address that points nowhere, so
you could not start setting values to its fields. For a struct, however, the
variable declaration actually allocates space on the stack for the entire
struct, so it's ready to assign values to. Note, however, that the following
code would cause a compilation error, with the compiler complaining that you
are using an uninitialized variable:
Dimensions point;
Double D = point.Length;
Structs
follow the same rules as any other data type: everything must be initialized
before use. A struct is considered fully initialized either when the new
operator has been called against it, or when values have been individually
assigned to all its fields. And of course, a struct defined as a member field
of a class is initialized by being zeroed-out automatically when the containing
object is initialized.
The
fact that structs are value types will affect performance, though depending on
how you use your struct, this can be good or bad. On the positive side,
allocating memory for structs is very fast because this takes place inline or
on the stack. The same goes for removing structs when they go out of scope. On
the other hand, whenever you pass a struct as a parameter or assign a struct to
another struct (as in A=B, where A and B are structs), the full contents of the
struct are copied, whereas for a class only the reference is copied. This will
result in a performance loss that depends on the size of the struct — this
should emphasize the fact that structs are really intended for small data
structures. Note, however, that when passing a struct as a parameter to a
method, you can avoid this performance loss by passing it as a ref parameter —
in this case only the address in memory of the struct will be passed in, which
is just as fast as passing in a class. On the other hand, if you do this,
you'll have to be aware that it means the called method can in principle change
the value of the struct.
Structs
are not designed for inheritance. This means that it is not possible to inherit
from a struct. The only exception to this is that structs, in common with every
other type in C#, derive ultimately from the class System.Object. Hence,
structs also have access to the methods of System.Object, and it is even
possible to override them in structs — an obvious example would be overriding
the ToString() method. The actual inheritance chain for structs is that each
struct derives from a class, System.ValueType, which in turn derives from
System.Object. ValueType does not add any new members to Object, but provides
implementations of some of them that are more suitable for structs. Note that
you cannot supply a different base class for a struct: Every struct is derived
from ValueType.
You
can define constructors for structs in exactly the same way that you can for
classes, except that you are not permitted to define a constructor that takes
no parameters. This may seem nonsensical, and the reason is buried in the
implementation of the .NET runtime. Some rare circumstances exist in which
the.NET runtime would not be able to call a custom zero-parameter constructor
that you have supplied. Microsoft has therefore taken the easy way out and
banned zero-parameter constructors for structs in C#.
That
said, the default constructor, which initializes all fields to zero values, is
always present implicitly, even if you supply other constructors that take
parameters. It's also impossible to circumvent the default constructor by
supplying initial values for fields. The following code will cause a
compile-time error:
struct Dimensions
{
public double Length = 1 // error. Initial values not
allowed
public double Width = 2 // error. Initial values not
allowed
Of course, if Dimensions had been
declared as a class, this code would have compiled without any problems.
Incidentally, you can supply a
Close() or Dispose() method for a struct in the same way you do for a class.
Example
program
using System;
struct student
{
public string name;
public int rollnumber;
public int marks;
public student(string
n, int r, int
m)
{
name = n;
marks = m;
rollnumber = r;
}
}
class Program
{
static void Main(string[]
args)
{
student
st = new student("Ramesh",1,85);
Console.WriteLine("Marks are",st.marks);
}
}
Nested Structures
C#
permits declaration of structs nested inside other structs.The following code
is valid
using System;
struct student
{
public string name;
public int rollnumber;
public int marks;
public
student(string n, int
r, int m)
{
name = n;
marks = m;
rollnumber = r;
}
public struct Ramesh
{
public
string address;
}
}
class Program
{
static void Main(string[]
args)
{
student
st = new student("Nikhil",13,78);
student.Ramesh ob;
ob.address = "Meerut";
Console.WriteLine("Ramesh's address:{0}",ob.address);
Console.WriteLine("Nikhil's Marks:{0}", st.marks);
}
}
We can also use struct variables as
members of another struct.This implies nesting of references to structs.
using System;
struct A
{
public int x;
}
struct B
{
public
A a;//object of A
public int y;
}
class Program
{
static void Main(string[]
args)
{
B ob;
ob.a.x=100;//’x’ is a member of ‘a’ and ‘a’ is
a member of ‘ob’
ob.y=400;
}
}