#520 – Choosing Between a struct and a Class

struct and a class both encapsulate data and methods in a new type.  When you are creating a new type, you’ll generally create a new class.  But there are cases when a struct is the better choice.

Create a struct, rather than a class, when all of the following is true:

  • You want value type semantics, that is–a variable of this type directly contains the data and a copy is made whenever you assign the value to a new variable or pass the variable to a method
  • Data stored in the struct won’t be modified after an instance is created or does not change very often
  • You don’t need to inherit from another type (a struct inherits only from System.ValueType)
  • You need to store only a small amount of data in the type

#519 – Differences Between structs and classes

There are a number of differences between a struct and a class, including:

  • A struct is a value type (instance created on the stack); a class is a reference type (instance created on the heap)
  • A variable defined as a struct type contains the actual data in the struct; a variable defined as a class type references or points to the data stored in the instance of the class
  • Memory for a struct is released when its variable goes out of scope; memory for a class instance is released when the object is garbage collected
  • When a struct is assigned to a new variable, a copy is made (changes to the original are not reflected in the copy); when an instance of a class is assigned to a new variable, the new variable references the existing instance
  • When a struct is passed to a method, a copy is made

#518 – Splitting the Implementation of a Class Across Multiple Files

It is sometimes convenient to split the source code for a class across more than one source file.  You can do this using the partial keyword.

In the example below, we split the implementation of the Dog class between Dog.cs and Dog-IBark.cs.  The latter file contains the implementation of the IBark interface.  The class definition in both files is marked with the partial keyword.

    // Dog.cs
    public partial class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }

        /// <summary>
        /// Create new Dog with specified name
        /// </summary>
        /// <param name="name"></param>
        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }

 

    partial class Dog : IBark
    {
        public bool CanBark { get; set; }

        public void Bark()
        {
            Console.WriteLine("WOOOOOF!");
        }
    }

Notice that we don’t specify IBark inheritance in the first file, because it does not implement any IBark methods.

#517 – Static Classes

A static class is a class that contains only static members and cannot be instantiated.  You declare a class as static using the static keyword.

A static class:

  • Can only have static members (can’t contain instance members)
  • Cannot be instantiated
  • Cannot serve as the type of a variable
  • Cannot serve as a parameter type
  • Cannot inherit from another class
  • Cannot serve as a parent of another class
    public static class DogMethods
    {
        private static int NumBarks = 5;

        public static void BarkALot(Dog d)
        {
            for (int i = 1; i <= NumBarks; i++)
                d.Bark();
        }

        public static string MarriedName(Dog d1, Dog d2)
        {
            return d1.Name + d2.Name;
        }
    }
            Dog d1 = new Dog("Kirby", 12);
            Dog d2 = new Dog("Lassie", 47);

            DogMethods.BarkALot(d1);
            Console.WriteLine(DogMethods.MarriedName(d1, d2));

#516 – The Assignment Operator is Right-Associative

An expression can contain more than one assignment operator.  If this is the case, the assignments are evaluated from right to left.  Consider the code fragment below.

int x = 12;
int z = 24;
int i = x = z;

Because the assignments are done from right to left, the variable x is first assigned the value that is stored in z (24).  At this point, both x and z have the value of 24.

Next, i is assigned the value that is the result of the first assignment (24).  At this point, x, z and i now all have the value of 24.

Because this behavior can be a little confusing,  it’s generally preferable to do each assignment on a separate line.

int x = 12;
int z = 24;
x = z;
int i = x;

#515 – Binary Operators Are Left-Associative

When an expression contains more than one binary operators, where the operators are identical or have the same precedence, the operators are left-assocative.  This means that the expression is evaluated from left to right.

For example, the result of the expression shown below is 5, rather than 20.  80 is divided by 8 to get an intermediate result of 10.  10 is then divided by 2 to get a result of 5.

            double result = 80 / 8 / 2;

This means that the above expression is equivalent to:

            double result = (80 / 8) / 2;

If you want to force the division of the 2nd and 3rd operands to happen first, you could use parentheses around them:

            // result = 20
            double result = 80 / (8 / 2);

#514 – Examples of Operator Precedence

Each operator has an associated precedence, which indicates the order in which the operators are evaluated when evaluating the  expression.

For example, because multiplicative (*, /, %) operators have a higher precedence than additive (+, -) operators, the multiplication in the expression below happens before the addition, so the answer is 34.

int result = 4 + 5 * 6;

If we want the addition to happen first, we can change the precedence by using parentheses.

// Result = 54
int result = (4 + 5) * 6;

Here are some other examples of operator precedence.

            // Negation operator has higher precedence than conditional operators
            bool res = !false || true;    // true  (negation operator evaluated first)
            res = !(false || true);       // false (conditional OR evaluated first)

            // && has higher precedence than ||
            bool res = true || false && false;    // true
            res = (true || false) && false;       // false

#513 – Some Familiar Methods that Accept Parameter Arrays

There are a number of methods in classes in the .NET Framework that accept parameter arrays, allowing you to pass a variable number of arguments to them.  Here are some examples:

String.Format:

int ft = 5;
int inches = 2;
string eyeColor = "blue";
string exclamation = "oh";
// string string.Format(string format, params object[] args)
string lyric = string.Format("{0} ft {1}, eyes of {2}; but {3} what those {0} foot could do", ft, inches, eyeColor, exclamation);

Console.WriteLine:

            string[] favs = {"Kittens", "Mittens", "Packages", "Tractors", "Streudel"};
            // void Console.WriteLine(string format, params object[] args)
            Console.WriteLine("Fav things: {0}, {1}, {2}, {3}, {4}", favs);

(Note: Console.WriteLine has an overload that accepts four object parameters after the format string, so the version with the parameter array doesn’t kick in until you use at least five parameters).

Path.Combine:

// string Path.Combine(params string[] paths)
string path = Path.Combine("D:\\", "Sean", "Notes", "WCF");

#512 – Two Ways to Pass Data to a Method that Takes a Parameter Array

Defining a parameter array using the params keyword allows you to pass a variable number of arguments to a method.

public void PerformCommands(params string[] commands)

You can pass a variable number of arguments to a method that has a parameter array.

            kirby.PerformCommands("Sit", "Stay", "Beg");

You can also just pass an array of the proper type.

            string[] tricks = new string[] {"Roll over", "Weave"};

            kirby.PerformCommands(tricks);

#511 – Rules for Using Parameter Arrays

You can use the params keyword when declaring a method to define a parameter array, allowing you to pass a variable number of parameters to the method.

public void PerformCommands(params string[] commandList)
{
    foreach (string command in commandList)
        Console.WriteLine("Ok, I'm doing [{0}]", command);
}

You can then pass any number of parameters to the method.  Every parameter that you specify is added to the single array that is passed to the method.

kirby.PerformCommands(new string[] { "Sit", "Stay", "Come" });

When using the params keyword:

  • You can only define one parameter array for a given method
  • The parameter must be the last parameter specified
  • The array must be a single-dimensional array
  • You can’t use the ref or out modifiers on the parameter array or on any of the arguments
  • Each argument must be implicitly convertible to the type of the array’s elements