#526 – Using Statements Can Alias Nested Namespaces

If you have a type defined in a nested namespace, e.g. type DogLogger defined in the DogLibrary.Utility namespace, you can include a using statements that aliases the inner namespace.

In the example below, we have two using statements.  One provides an alias for the outer DogLibrary namespace, so that you can use types from the DogLibrary namespace by just using the type name.  The second aliases the DogLibrary.Utility namespace, so that you can use types defined in the inner namespace.

using DogLibrary;
using DogLibrary.Utility;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            // DogLibrary.Dog
            Dog d = new Dog();

            // DogLibrary.Utility.DogLogger
            DogLogger logger = new DogLogger();
        }
    }

#525 – Namespaces Can Be Nested

You can define a namespace at the top-level of a source file or you can define a namespace within another namespace.  When you define a namespace within another namespace, you can refer to the inner namespace using a dotted (outer.inner) syntax.

In the example below, we have a Dog class defined within the top-level DogLibrary namespace.  The full name for this type is DogLibrary.Dog.  We also define a Utility namespace within the DogLibrary namespace, and a DogLogger class whose full name is then DogLibrary.Utility.DogLogger.

namespace DogLibrary
{
    public class Dog
    {
    }

    namespace Utility
    {
        public class DogLogger
        {
        }
    }
}

#524 – All Types Within a Namespace Must Be Unique

You can create more than one type with the same name, as long as the exist in different namespaces.  But within a particular namespace, the name of every type must be unique.

In the example below, we declare a Dog class in both the EarthDogs and AlienDogs namespaces.

namespace EarthDogs
{
    public class Dog
    {
        public string Name { get; set; }

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

namespace AlienDogs
{
    public class Dog
    {
        public string Name { get; set; }

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

(In practice, for these classes, you’d probably instead declare a Dog parent class and subclasses EarthDog and AlienDog, which would override the Bark method).

#523 – The using Directive Allows Omitting Namespace

A typical C# program will use types that are defined in a variety of namespaces.  Specifying the fully qualified type name that includes the namespace can become tedious.

using directive tells the compiler what namespaces to look for types in, avoiding the need for fully qualified type names.

Assume that we have a Dog type, defined in the DogLibrary namespace.  The fully qualified type name is DogLibrary.Dog.  But in the code fragment below, we can just use Dog as the type name, because of the using directive.

using DogLibrary;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Dog kirby = new Dog();
            kirby.Bark();
        }
    }
}

So you can use a type name without its namespace if:

  • The type is defined in the same namespace as the current code
  • using directive for the type’s namespace is present

#522 – The Fully Qualified Name for a Type Includes the Namespace

If we define a Dog type in the DogLibrary namespace, the fully qualified name for the new type is DogLibrary.Dog.  You can use this full name when working with the type.

            DogLibrary.Dog kirby = new DogLibrary.Dog();
            kirby.Bark();

If you’re writing code that exists in the same namespace as a type, you can omit the namespace and just use the short version of the type name.

namespace DogLibrary
{
    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }

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

    public static class DogFactory
    {
        public static Dog MakeDog(string name, int age)
        {
            // Can just use "Dog" as type name
            Dog d = new Dog();
            d.Name = name;
            d.Age = age;
            return d;
        }
    }
}

#521 – Namespaces Help Organize Types

In C#, a namespace is a collection of related types.  The fully qualified name of every type actually includes the namespace that it’s defined in.

You use the namespace keyword to define a namespace.  All types defined within the pair of braces that follow the namespace name then belong to that namespace.

In the example below, the Dog class belongs to the DogLibrary namespace.

namespace DogLibrary
{
    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }

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

Because Dog is defined within the DogLibrary namespace, the fully qualifed name of the Dog type is DogLibrary.Dog.

#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

#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);

Follow

Get every new post delivered to your Inbox.

Join 37 other followers