C# Day 2 - Objects and Types (part 3)

49 views

Anonymous Types

Chapter 2 discussed the var keyword in reference to implicitly typed variables. When used with the
new keyword, anonymous types can be created. An anonymous type is simply a nameless class
that inherits from object . The definition of the class is inferred from the initializer, just like in
implicitly typed variables.
If you needed an object that contained a person ’ s first, middle, and last name the declaration would look
like this:

 var captain = new {FirstName = “James”, MiddleName = “T”, LastName = “Kirk”};

This would produce an object with FirstName , MiddleName , and LastName properties. If you were to
create another object that looked like this:

 var doctor = new {FirstName = “Leonard”, MiddleName = “”, LastName = “McCoy”};

The types of captain and doctor are the same. You could set captain = doctor , for example.

If the values that are being set come from another object, then the initializer can be abbreviated. If you
already have a class that contains the properties FirstName , MiddleName , and LastName and you have
an instance of that class with the instance name person , then the captain object could be initialized
like this:

   var captain = new (person.FirstName, person.MidleName, person.LastName};

The property names from the person object would be projected to the new object named captain . So
the object named captain would have the FirstName , MiddleName , and LastName properties.
The actual type name of these new objects is unknown. The compiler “ makes up ” a name for the type,
but only the compiler will ever be able to make use of it. So you can ’ t and shouldn ’ t plan on using any
type reflection on the new objects because you will not get consistent results.

Structs

So far, you have seen how classes offer a great way of encapsulating objects in your program. You have
also seen how they are stored on the heap in a way that gives you much more flexibility in data lifetime,
but with a slight cost in performance. This performance cost is small thanks to the optimizations of
managed heaps. However, 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. All you have is two numbers, which you ’ ll find
convenient to treat as a pair rather than individually. There is no need for a lot 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 double s.
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;
   }
   public double Diagonal
   {
   get
   {
    return Math.Sqrt(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 memory (this is examined in
Chapter 13 , “ Reflection, ” which covers attributes).
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.
C++ developers beware — structs in C# are very different from classes in their implementation. This is
unlike C++, in which classes and structs are virtually the same thing.
The following sections look at some of these differences between structs and classes in more detail.

Structs Are Value Types

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;
  point.Width = 6;

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 negative side, 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,
emphasizing 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. If you do this, though, be aware that it means the called method can in principle
change the value of the struct.

Structs and Inheritance

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 .

Constructors for Structs

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.

Partial Classes

The partial keyword allows the class, struct, or interface to span across multiple files. Typically, a class
will reside entirely in a single file. However, in situations where multiple developers need access to the
same class, or more likely in the situation where a code generator of some type is generating part of a
class, then having the class in multiple files can be beneficial.
The way that the partial keyword is used is to simply place partial before class, struct , or
interface . In the following example the class TheBigClass resides in two separate source files,
BigClassPart1.cs and BigClassPart2.cs :

  //BigClassPart1.cs
    partial class TheBigClass
    {
     public void MethodOne()
     {
    }
  }
    //BigClassPart2.cs
    partial class TheBigClass
     {
     public void MethodTwo()
      {
   }
 }

When the project that these two source files are part of is compiled, a single type called TheBigClass
will be created with two methods, MethodOne() and MethodTwo() .
If any of the following keywords are used in describing the class, the same must apply to all partials of
the same type:
❑ public
❑ private
❑ protected
❑ internal
❑ abstract
❑ sealed
❑ new
❑ generic constraints

Nested partials are allowed as long as the partial keyword precedes the class keyword in the nested
type. Attributes, XML comments, interfaces, generic - type parameter attributes, and members will be
combined when the partial types are compiled into the type. Given the two source files:

  //BigClassPart1.cs
     [CustomAttribute]
      partial class TheBigClass : TheBigBaseClass, IBigClass
      {
       public void MethodOne()
       {
     }
  }
      //BigClassPart2.cs
      [AnotherAttribute]
      partial class TheBigClass : IOtherBigClass
       {
         public void MethodTwo()
           {
        }
     }

After the compile, the equivalent source file would be:

   [CustomAttribute]
   [AnotherAttribute]
  partial class TheBigClass : TheBigBaseClass, IBigClass, IOtherBigClass
   {
    public void MethodOne()
     {
     }
      public void MethodTwo()
      {
    }
 }

Static Classes

Earlier, this chapter discussed static constructors and how they allowed the initialization of static
member variables. If a class contains nothing but static methods and properties, the class itself can
become static. A static class is functionally the same as creating a class with a private static constructor.
An instance of the class can never be created. By using the static keyword, the compiler can help by
checking that instance members are never accidentally added to the class. If they are, a compile error
happens. This can help guarantee that an instance is never created. The syntax for a static class looks
like this:

   static class StaticUtilities
    {
     public static void HelperMethod()
      {
     }
  }

An object of type StaticUtilities is not needed to call the HelperMethod() . The type name is used
to make the call:

    StaticUtilities.HelperMethod();



« Chapter 3..PHP Basics (Part-3) C# Day 2 - Objects and Types (part 4) »
Posted on Monday, February 9th, 2009 at 4:52 pm under Professional C sharp | RSS 2.0 Feed

Post Comment

You must be logged in to post a comment.



ComputerEducationWorld.com All Rights Reserved © RSS | CBSE | Education Boards Of India | What is My IP?