#328 – Copying a struct Will Not Copy Underlying Reference Types

Assigning a struct to a new variable will make a copy of the contents of the struct.  After the assignment, two copies of the struct will exist, which can be changed independently.

If the struct contains any reference-typed fields, only a reference to an object is copied.  Both copies of the struct will then refer to the same object.

Assume that we define the following struct:

        public struct MovieInfo
        {
            public string Title;
            public int Year;
            public Person Director;
        }

If we make a copy of the MovieInfo struct through an assignment, the Director field in the two copies will point to the same Person object.

            MovieInfo goneWithWind;
            goneWithWind.Title = "Gone With the Wind";
            goneWithWind.Year = 1939;
            goneWithWind.Director = new Person("Victor", "Fleming");

            // Actually makes a copy of entire struct
            MovieInfo goodClarkGableFlick = goneWithWind;

            // Change director's name
            goneWithWind.Director.FirstName = "Victoria";

            Console.WriteLine(goneWithWind.Director);            // Victoria Fleming
            Console.WriteLine(goodClarkGableFlick.Director);     // Victoria Fleming

#327 – Assigning a struct to a New Variable Makes a Copy

Because a struct is a value type, you make a copy of the entire struct when you assign it to a new variable.

Assume that we define the following struct:

public struct MovieInfo
{
    public string Title;
    public int Year;
    public string Director;
}

If we define a variable of this type and then assign it to another variable, the second variable gets a copy of all of the data. If we then change some field in the first copy, the second copy is unchanged.

            MovieInfo goneWithWind;
            goneWithWind.Title = "Gone With the Wind";
            goneWithWind.Year = 1938;
            goneWithWind.Director = "Victor Fleming";

            // Actually makes a copy of entire struct
            MovieInfo goodClarkGableFlick = goneWithWind;

            // Fix the year
            goneWithWind.Year = 1939;

            Console.WriteLine(goodClarkGableFlick.Year);    // Oops, still 1938

#326 – Generic Type vs. Constructed Type

Once a generic type is provided with type arguments, it is known as a constructed type.

Here is the definition of a generic type:

    // A generic type
    public class ThingContainer<TThing1, TThing2>
    {
        public TThing1 Thing1;
        public TThing2 Thing2;
    }

You declare instances of the generic type by providing arguments for its type parameters.  The type name with the arguments is the constructed type.

            // ThingContainer<Dog, DateTime> is a constructed type
            ThingContainer<Dog, DateTime> container = new ThingContainer<Dog, DateTime>();

            container.Thing1 = new Dog("Bob");
            container.Thing2 = DateTime.Now;

#325 – Intellisense Understands Generic Classes

When using a generic class, Intellisense understands the type parameters passed to the class when it was constructed.  It will display the correct types when showing information for any method or property of the generic class.

Suppose we have a generic class defined as follows:

    public class ThingContainer<TThing1, TThing2>
    {
        private TThing1 thing1;
        private TThing2 thing2;

        public void SetThings(TThing1 first, TThing2 second)
        {
            thing1 = first;
            thing2 = second;
        }
    }

And suppose that we use the class as follows:

            ThingContainer<Dog, DateTime> container = new ThingContainer<Dog, DateTime>();

Now when we start entering the name of the ThingContainer.SetThings method, Intellisense displays the correct types for the method’s parameters.

#324 – A Generic Class Can Have More than One Type Parameter

A generic class includes one or more type parameters that will be substituted with actual types when the class is used.  If the class has more than one type parameter, all parameters must be substituted when the class is used.

Here’s an example of a generic class that stores two objects, each having its own type.

    public class ThingContainer<TThing1, TThing2>
    {
        private TThing1 thing1;
        private TThing2 thing2;

        public void SetThings(TThing1 first, TThing2 second)
        {
            thing1 = first;
            thing2 = second;
        }

        public string DumpThings()
        {
            return string.Format("{0}, {1}", thing1.ToString(), thing2.ToString());
        }
    }

We can use this class as follows:

            ThingContainer<string,int> cont1 = new ThingContainer<string,int>();
            cont1.SetThings("Hemingway", 1899);
            Console.WriteLine(cont1.DumpThings());      //  Hemingway, 1899

            ThingContainer<Dog, DateTime> cont2 = new ThingContainer<Dog, DateTime>();
            cont2.SetThings(new Dog("Kirby", 14), new DateTime(1998, 5, 1));
            Console.WriteLine(cont2.DumpThings());      // Kirby (14 yrs), 5/1/1998 12:00:00 AM

#323 – A Generic Class is a Template for a Class

A generic class is a class that takes one or more type parameters, which it then uses in the definition of the class.  It can be thought of as a template for a class.

    public class ThingContainer<TParam>
    {
        private TParam theThing;

        public void SetThing(TParam newValue)
        {
            theThing = newValue;
        }
    }

You use a generic class by specifying a type for each of the type parameters.

            ThingContainer<int> intContainer = new ThingContainer<int>();
            intContainer.SetThing(5);

            ThingContainer<Dog> dogContainer = new ThingContainer<Dog>();
            dogContainer.SetThing(new Dog("Kirby", 5));

In this example, we use a generic class to store an object of an arbitrary type.  We use one version of the class to store an int and another to store a Dog.  Notice that wherever we use the name of the generic class to define an instance, we need to supply a typename (e.g. int, Dog) as a parameter.

#322 – Class Accessibility

The members of a class all have an associated access modifier, which defines their accessibility.  A class itself also has an accessibility level, which dictates which code can make use of the class.

The two types of accessibility for a class are:

  • public – all code can use the class
  • internal – only code in the same .exe or .dll can use the class

In the code below, the Dog class, defined in DogLibrary.dll, is marked as internal.  This means that only code within the same DLL (highlighted blue) can create and use instances of the Dog class.

The Program class in Program.exe has access to the DogKennel class, but not the Dog class.  It can create an instance of a DogKennel, but not an instance of a Dog.

    class Program
    {
        static void Main()
        {
            // Ok
            DogKennel k = new DogKennel();
            k.EverybodyBark();

            // ERROR
            Dog d = new Dog("Kirby");