Understanding C# Features (1) Fundamentals

Datetime:2016-08-22 22:11:13          Topic: C#           Share

This part introduces the basic C# syntax used in this tutorial for beginners.

Types and members

C# is a strongly typed language. In C#, any value has a type. C# and .NET support 5 kinds of types : class, structure, enumeration, delegate, and interface.

A class is a reference type derived from System.Object class. It can have fields, properties, methods, events, operators, indexers, constructors, destructor, and nested class, structure, enumeration, delegate, and interface types.

namespace System
{
    [Serializable]
    public class Exception : ISerializable, _Exception
    {
        internal string _message; // Field.
        
        private Exception _innerException; // Field.

        [OptionalField(VersionAdded = 4)]
        private SafeSerializationManager _safeSerializationManager; // Field.

        public Exception InnerException { get { return this._innerException; } } // Property.

        public Exception(string message, Exception innerException) // Constructor.
        {
            this.Init();
            this._message = message;
            this._innerException = innerException;
        }

        public virtual Exception GetBaseException() // Method.
        {
            Exception innerException = this.InnerException;
            Exception result = this;
            while (innerException != null)
            {
                result = innerException;
                innerException = innerException.InnerException;
            }
            return result;
        }

        protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState // Event.
        {
            add
            {
                this._safeSerializationManager.SerializeObjectState += value;
            }
            remove
            {
                this._safeSerializationManager.SerializeObjectState -= value;
            }
        }

        internal enum ExceptionMessageKind // Nested enumeration type.
        {
            ThreadAbort = 1,
            ThreadInterrupted,
            OutOfMemory
        }

        // Other members.
    }
}

A structure is value type derived from System.ValueType class, which is derived from System.Object class. It can have all kinds of members of class except destructor. In practice, a structure is usually defined to represent very small and immutable data structure, in order to improve the performance of memory allocation and deallocation.

namespace System
{
    public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable<TimeSpan>, IFormattable
    {
        public const long TicksPerMillisecond = 10000; // Field

        public const long TicksPerSecond = TicksPerMillisecond * 1000; // Field

        internal long _ticks; // Field

        private const double SecondsPerTick = 1.0 / TicksPerSecond; // Field

        public TimeSpan(long ticks) // Constructor.
        {
            this._ticks = ticks;
        }

        public int Seconds { get { return (int)((this._ticks / TicksPerSecond) % 60); } } // Property.

        public double TotalSeconds { get { return this._ticks * SecondsPerTick; } } // Property.

        public static bool operator ==(TimeSpan t1, TimeSpan t2) // Operator.
        {
            return t1._ticks == t2._ticks;
        }

        // Other members.
    }
}

An enumeration is a value type derived from System.Enum class, which is derived from System.ValueType class. It can only have constant fields of the specified underlying integral type (System.Int32 by default)

namespace System
{
    public enum DayOfWeek
    {
        Sunday = 0,
        Monday = 1,
        Tuesday = 2,
        Wednesday = 3,
        Thursday = 4,
        Friday = 5,
        Saturday = 6,
    }
}

A delegate is a reference type derived from System.MulticastDelegate class, which is derived from System.Delegate class. Delegate will be discussed in detail later.

namespace System
{
    public delegate void Action();
}

An interface is a contract to be implemented by class or structure. Interface can only have abstract properties, methods, and events.

namespace System
{
    public interface ICloneable
    {
        object Clone();
    }

    public class String : ICloneable // Other interfaces: IComparable, IConvertible ...
    {
        public object Clone() // From ICloneable.
        {
            return this;
        }
    }
}

Built-in types

There are .NET FCL types commonly used in C# programming, so C# provide language keywords as aliases for those type names. They are called C#’s built-in types:

C# keyword .NET FCL type
bool System.Boolean
sbyte System.SByte
byte System.Byte
char System.Char
short System.Init16
ushort System.UInit16
int System.Init32
uint System.UInit32
long System.Init54
ulong System.UInit54
float System.Single
double System.Double
decimal System.Decimal
object System.Object
string System.String

IDisposable and using statement

At runtime, .NET CLR manages memory automatically. It allocate memory for CLR objects and release the memory with garbage collector. A CLR object can allocate other resources unmanaged by .NET CRL, like open files, window handles, database connections, etc. .NET provides a standard contract for these objects:

namespace System
{
    [ComVisible(true)]
    public interface IDisposable
    {
        void Dispose();
    }
}

A type implementing System.IDisposable interface must have a Dispose method to explicitly release its unmanaged resources. For example, System.Data.SqlClient.SqlConnection class represents a connection to a SQL database, it implements IDisposable and provides Dispose method to close and release the represented database connection. The following example is the standard try-finally pattern to use IDisposable object and Dispose method:

internal static partial class Syntax
{
    internal static void Dispose()
    {
        SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\MSSQLLocalDB");
        try
        {
            // Use connection object.
            connection.Open();
            Trace.WriteLine(connection.ServerVersion); // 13.00.1708
        }
        finally
        {
            if (connection != null)
            {
                ((IDisposable)connection).Dispose();
            }
        }
    }
}

The Dispose method is in finally block, so it is ensured to be called, even if exception is thrown when using the SqlConnection object, or if the current thread is aborted. IDisposable objects are widely used, so C# provides a using statement syntax since 1.0. The above code is equivalent to:

internal static void Using()
{
    using (SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\MSSQLLocalDB"))
    {
        // Use connection object.
        connection.Open();
        Trace.WriteLine(connection.ServerVersion); // 13.00.1708
    }
}

IDisposable object should be always used with this syntax to ensure it is disposed after usage in the right way.

Static class

C# 2.0 enables static keyword for class definition:

namespace System
{
    public static class Math
    {
        // Static members only.
    }
}

A static class can only have static members, and cannot be instantiated to objects. Static class is compiled to abstract sealed class.

Partial class

C# 2.0 provides the partial keyword to split the definition of class, structure, or interface:

internal partial class Device
{
    private string name;

    internal string Name
    {
        get { return this.name; }
        set { this.name = value; }
    }
}

internal partial class Device
{
    public override string ToString()
    {
        return this.Name;
    }
}

Auto property

A property is essentially a getter method and/or a setter method, like above Device class’ Name property. It can be annoying when a class has many properties for data fields, so C# 3.0 supports auto property with body omitted:

internal partial class Device
{
    internal decimal Price { get; set; }
}

The compiler generates the field definition and getter/setter body for auto property:

internal class CompiledDevice
{
    [CompilerGenerated]
    private decimal priceBackingField;

    internal decimal Price
    {
        [CompilerGenerated]
        get { return this.priceBackingField; }

        [CompilerGenerated]
        set { this.priceBackingField = value; }
    }

    // Other members.
}

Since C# 6.0, private setter can be omitted, the following Name property is called getter only auto property:

internal partial class Category
{
    internal Category(string name)
    {
        this.Name = name;
    }

    internal string Name { get; /* private set; */ }
}

Property initializer

Getter only auto property can be initialized from constructor, or from property initializer:

internal partial class Category
{
    internal Guid Id { get; } = Guid.NewGuid();
}

Above Category class is compiled to:

internal class CompiledCategory
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly Guid idBackingField = Guid.NewGuid();

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string nameBackingField;

    internal CompiledCategory(string name)
    {
        this.nameBackingField = name;
    }

    internal string Name
    {
        [CompilerGenerated]
        get { return this.nameBackingField; }
    }

    internal Guid Id
    {
        [CompilerGenerated]
        get { return this.idBackingField; }
    }
}

Object initializer

Before C# 3.0, a Device object can be initialized with a sequence of imperative statements:

internal static void SetProperties()
{
    Device device = new Device();
    device.Name = "Surface Book";
    device.Price = 1499;
}

C# 3.0 introduce object initializer syntax, above call constructor and set properties code can be more declarative:

internal static void ObjectInitializer()
{
    Device device = new Device() { Name = "Surface Book", Price = 1499 };
}

The second version is compiled to the first version.

Collection initializer

Similarly, C# 3.0 provides collection initializer syntax. If a collection has a Add method accepting parameters, and also implement System.Collections.IEnumerable interface (has a GetEnumerator method):

internal class DeviceCollection : IEnumerable
{
    private Device[] devices = new Device[0];

    internal void Add(Device device)
    {
        Array.Resize(ref this.devices, this.devices.Length + 1);
        this.devices[this.devices.Length - 1] = device;
    }

    public IEnumerator GetEnumerator() // From IEnumerable.
    {
        return this.devices.GetEnumerator();
    }
}

Then it can be initialized declaratively too:

internal static void CollectionInitializer(Device a, Device b)
{
    DeviceCollection devices = new DeviceCollection { a, b };
}

The above code is compiled to normal imperative constructor call and Add method calls:

internal static void AddToCollections(Device a, Device b)
{
    DeviceCollection devices = new DeviceCollection();
    devices.Add(a);
    devices.Add(b);
}

Index initializer

Since C# 6.0, index initializer is also provided for types with indexer:

internal class DeviceDictionary
{
    internal Device this[int id] { set { } }
}

It is similar declarative syntax:

internal static void IndexInitializer(Device a, Device b)
{
    DeviceDictionary devices = new DeviceDictionary { [0] = a, [1] = b };
}

Again, the above code is compiled to normal imperative constructor call and index setter calls:

internal static void SetIndexer(Device a, Device b)
{
    DeviceDictionary devices = new DeviceDictionary();
    devices[0] = a;
    devices[1] = b;
}

Null coalescing operator

When working with nullable value, it is very common to have null check at runtime, and replace it if it is null:

internal static void DefaultValueForNull(Uri nullableValue)
{
    Uri value;
    if ((object)nullableValue != null)
    {
        value = nullableValue;
    }
    else
    {
        value = new Uri("https://localhost"); // Default value for null.
    }
    // value is not null.
}

C# 2.0 provides a null coalescing operator ?? to implement the above logic equivalently:

internal static void NullCoalescing(Uri nullableValue)
{
    Uri value = nullableValue ?? new Uri("http://localhost");
    // value is not null.
}

Null conditional operator

When working with nullable value, it is also very common to check null before accessing its member, like calling its method:

internal static void MemberAccess(Uri nullableValue)
{
    string result;
    if ((object)nullableValue != null)
    {
        result = nullableValue.ToString();
    }
    else
    {
        result = null;
    }
}

C# 6.0 provides a null conditional operator (sometimes called null propagation operator) to implement the above logic equivalently:

internal static void NullConditional(Uri nullableValue)
{
    string result = nullableValue?.ToString();
}

String interpolation

For many years, string formatting is widely used in C#. It inserts values to placeholders in string format:

internal static int AddWithLog(int a, int b)
{
    int sum = a + b;
    Trace.WriteLine(string.Format("{0}: {1} + {2} => {3}", DateTime.Now.ToString("o"), a, b, sum));
    return sum;
}

C# 6.0 provides string interpolation to have the values in place:

internal static int StringInterpolation(int a, int b)
{
    int sum = a + b;
    Trace.WriteLine($"{DateTime.Now.ToString("o")}: {a} + {b} => {sum}");
    return sum;
}

nameof expression

C# 6.0 provides a nameof keyword to obtain the string name of variable, type, or member. Before C# 6.0, the following pattern is widely used for argument null check:

internal static void ArgumentCheck(Uri url)
{
    if (url == null)
    {
        throw new ArgumentNullException("url");
    }
}

The argument name will be in the exception message, and it has to be a hard coded string, and cannot be checked by compiler. Now the argument name can be obtained with nameof:

internal static void NameOf(Uri url)
{
    if (url == null)
    {
        throw new ArgumentNullException(nameof(url));
    }
}

The nameof expression is compiled to a constant string. so the second version is compiled to first version.





About List