[ Team LiB ] |
7.2 Creating StructsCreate an instance of a struct by using the new keyword in an assignment statement, just as you would for a class. In Example 7-1, the Tester class creates an instance of Location as follows: Location loc1 = new Location(200,300); Here the new instance is named loc1 and is passed two values, 200 and 300. 7.2.1 Structs as Value TypesThe definition of the Tester class in Example 7-1 includes a Location object (loc1) created with the values 200 and 300. This line of code calls the Location constructor: Location loc1 = new Location(200,300); Then WriteLine( ) is called: Console.WriteLine("Loc1 location: {0}", loc1); WriteLine( ) is expecting an object, but, of course, Location is a struct (a value type). The compiler automatically boxes the struct (as it would any value type), and it is the boxed object that is passed to WriteLine( ). ToString( ) is called on the boxed object, and because the struct (implicitly) inherits from object, it is able to respond polymorphically, overriding the method just as any other object might: Loc1 location: 200, 300 Structs are value objects, however, and when passed to a function, they are passed by value—as seen in the next line of code, in which the loc1 object is passed to the myFunc( ) method: t.myFunc(loc1); In myFunc( ), new values are assigned to x and y, and these new values are printed out: Loc1 location: 50, 100 When you return to the calling function (Main( )) and call WriteLine( ) again, the values are unchanged: Loc1 location: 200, 300 The struct was passed as a value object, and a copy was made in myFunc( ). Try changing the declaration to class: public class Location and run the test again. Here is the output: Loc1 location: 200, 300 In MyFunc loc: 50, 100 Loc1 location: 50, 100 This time the Location object has reference semantics. Thus, when the values are changed in myFunc( ), they are changed on the actual object back in Main( ). 7.2.2 Calling the Default ConstructorAs mentioned earlier, if you do not create a constructor, an implicit default constructor is called by the compiler. We can see this if we comment out the constructor: /* public Location(int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } */ and replace the first line in Main( ) with one that creates an instance of Location without passing values: // Location loc1 = new Location(200,300); Location loc1 = new Location( ); Because there is now no constructor at all, the implicit default constructor is called. The output looks like this: Loc1 location: 0, 0 In MyFunc loc: 50, 100 Loc1 location: 0, 0 The default constructor has initialized the member variables to zero. 7.2.3 Creating Structs Without newBecause loc1 is a struct (not a class), it is created on the stack. Thus, in Example 7-1, when the new operator is called: Location loc1 = new Location(200,300); the resulting Location object is created on the stack. The new operator calls the Location constructor. However, unlike with a class, it is possible to create a struct without using new at all. This is consistent with how built-in type variables (such as int) are defined, and is illustrated in Example 7-2.
Example 7-2. Creating a struct without using newusing System; public struct Location { public int xVal; public int yVal; public Location(int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } public int x { get { return xVal; } set { xVal = value; } } public int y { get { return yVal; } set { yVal = value; } } public override string ToString( ) { return (String.Format("{0}, {1}", xVal,yVal)); } } public class Tester { static void Main( ) { Location loc1; // no call to the constructor loc1.xVal = 75; // initialize the members loc1.yVal = 225; Console.WriteLine(loc1); } } In Example 7-2, you initialize the local variables directly, before calling a method of loc1 and before passing the object to WriteLine( ): loc1.xVal = 75; loc1.yVal = 225; If you were to comment out one of the assignments and recompile: static void Main( ) { Location loc1; loc1.xVal = 75; // loc1.yVal = 225; Console.WriteLine(loc1); } you would get a compiler error: Use of unassigned local variable 'loc1' Once you assign all the values, you can access the values through the properties x and y: static void Main( ) { Location loc1; loc1.xVal = 75; // assign member variable loc1.yVal = 225; // assign member variable loc1.x = 300; // use property loc1.y = 400; // use property Console.WriteLine(loc1); } Be careful about using properties. Although these allow you to support encapsulation by making the actual values private, the properties themselves are actually member methods, and you cannot call a member method until you initialize all the member variables. |
[ Team LiB ] |