The Philosophy of .NET - Introducing C# and the .NET Platform - C# 6.0 and the .NET 4.6 Framework (2015)

C# 6.0 and the .NET 4.6 Framework (2015)

PART I

image

Introducing C# and the .NET Platform

CHAPTER 1

image

The Philosophy of .NET

Microsoft’s .NET platform (and the related C# programming language) were formally introduced circa 2002 and have quickly become a mainstay of modern-day software development. As mentioned in the book’s introductory section, the goal of this text is twofold. The first order of business is to provide you with deep and detailed examination of the syntax and semantics of C#. The second (equally important) order of business is to illustrate the use of numerous .NET APIs, including database access with ADO.NET and the Entity Framework (EF), the LINQ technology set, WPF, WCF and web site development using ASP.NET. As they say, the journey of a thousand miles begins with a single step; and with this I welcome you to Chapter 1.

The point of this first chapter is to lay the conceptual groundwork for the remainder of the book. Here you will find a high-level discussion of a number of .NET-related topics such as assemblies, the Common Intermediate Language (CIL), and just-in-time (JIT) compilation. In addition to previewing some keywords of the C# programming language, you will also come to understand the relationship between various aspects of the .NET Framework, such as the Common Language Runtime (CLR), the Common Type System (CTS), and the Common Language Specification (CLS).

This chapter also provides you with a survey of the functionality supplied by the .NET base class libraries, sometimes abbreviated as BCLs. Here, you will also overview the language-agnostic and platform-independent nature of the .NET platform (yes, it’s true; .NET is not confined to the Windows operating system). As you would hope, many of these topics are explored in further detail throughout the remainder of this text.

An Initial Look at the .NET Platform

Before Microsoft released the C# language and .NET platform, software developers who created applications for the Windows family of operating system frequently made use of the COM programming model. COM (which stands for the Component Object Model) allowed individuals to build libraries of code that could be shared across diverse programming languages. For example, a C++ programmer could build a COM library that could be used by a Visual Basic developer. The language-independent nature of COM was certainly useful; however, COM was plagued by complicated infrastructure and a fragile deployment model and was possible only on the Windows operating system.

Despite the complexity and limitations of COM, countless applications have been successful created with this architecture. However, nowadays, a majority of applications created for the Windows family of operating systems are not created with the COM model. Rather, desktop applications, web sites, OS services, and libraries of reusable data access/business logic are created using the .NET platform.

Some Key Benefits of the .NET Platform

As mentioned, C# and the .NET platform were first introduced to the world in 2002 and were intended to offer a much more powerful, more flexible, and simpler programming model than COM. As you will see during the remainder of this book, the .NET Framework is a software platform for building systems on the Windows family of operating systems, as well as on numerous non-Microsoft operating systems such as Mac OS X and various Unix/Linux distributions. To set the stage, here is a quick rundown of some core features provided courtesy of .NET:

· Interoperability with existing code: This is (of course) a good thing. Existing COM software can commingle (i.e., interop) with newer .NET software, and vice versa. As of .NET 4.0 onward, interoperability has been further simplified with the addition of the dynamickeyword (covered in Chapter 16).

· Support for numerous programming languages: .NET applications can be created using any number of programming languages (C#, Visual Basic, F#, and so on).

· A common runtime engine shared by all .NET-aware languages: One aspect of this engine is a well-defined set of types that each .NET-aware language understands.

· Language integration: .NET supports cross-language inheritance, cross-language exception handling, and cross-language debugging of code. For example, you can define a base class in C# and extend this type in Visual Basic.

· A comprehensive base class library: This library provides thousands of predefined types that allow you to build code libraries, simple terminal applications, graphical desktop application, and enterprise-level web sites.

· A simplified deployment model: Unlike COM, .NET libraries are not registered into the system registry. Furthermore, the .NET platform allows multiple versions of the same *.dll to exist in harmony on a single machine.

You will see each of these topics (and many more) examined in the chapters to come.

Introducing the Building Blocks of the .NET Platform (the CLR, CTS, and CLS)

Now that you know some of the major benefits provided by .NET, let’s preview three key (and interrelated) topics that make it all possible: the CLR, CTS, and CLS. From a programmer’s point of view, .NET can be understood as a runtime environment and a comprehensive base class library. The runtime layer is properly referred to as the Common Language Runtime , or CLR. The primary role of the CLR is to locate, load, and manage .NET objects on your behalf. The CLR also takes care of a number of low-level details such as memory management, application hosting, coordinating threads, and performing basic security checks (among other low-level details).

Another building block of the .NET platform is the Common Type System , or CTS. The CTS specification fully describes all possible data types and all programming constructs supported by the runtime, specifies how these entities can interact with each other, and details how they are represented in the .NET metadata format (more information on metadata later in this chapter; see Chapter 15 for complete details).

Understand that a given .NET-aware language might not support every feature defined by the CTS. The Common Language Specification , or CLS, is a related specification that defines a subset of common types and programming constructs that all .NET programming languages can agree on. Thus, if you build .NET types that expose only CLS-compliant features, you can rest assured that all .NET-aware languages can consume them. Conversely, if you make use of a data type or programming construct that is outside of the bounds of the CLS, you cannot guarantee that every .NET programming language can interact with your .NET code library. Thankfully, as you will see later in this chapter, it is simple to tell your C# compiler to check all of your code for CLS compliance.

The Role of the Base Class Libraries

In addition to the CLR, CTS, and CLS specifications, the .NET platform provides a base class library that is available to all .NET programming languages. Not only does this base class library encapsulate various primitives such as threads, file input/output (I/O), graphical rendering systems, and interaction with various external hardware devices, but it also provides support for a number of services required by most real-world applications.

The base class libraries define types that can be used to build any type of software application. For example, you can use ASP.NET to build web sites and REST services, WCF to build distributed systems, WPF to build desktop GUI applications, and so forth. As well, the base class libraries provide types to interact with XML documents, the directory and file system on a given computer, communicate with a relational databases (via ADO.NET), and so forth. From a high level, you can visualize the relationship between the CLR, CTS, CLS, and the base class library, as shown in Figure 1-1.

image

Figure 1-1. The CLR, CTS, CLS, and base class library relationship

What C# Brings to the Table

C# is a programming language whose core syntax looks very similar to the syntax of Java. However, calling C# a Java clone is inaccurate. In reality, both C# and Java are members of the C family of programming languages (e.g., C, Objective C, C++) and, therefore, share a similar syntax.

The truth of the matter is that many of C#’s syntactic constructs are modeled after various aspects of Visual Basic (VB) and C++. For example, like VB, C# supports the notion of class properties (as opposed to traditional getter and setter methods) and optional parameters. Like C++, C# allows you to overload operators, as well as create structures, enumerations, and callback functions (via delegates).

Moreover, as you work through this text, you will quickly see that C# supports a number of features traditionally found in various functional languages (e.g., LISP or Haskell) such as lambda expressions and anonymous types. Furthermore, with the advent of Language Integrated Query(LINQ), C# supports a number of constructs that make it quite unique in the programming landscape. Nevertheless, the bulk of C# is indeed influenced by C-based languages.

Because C# is a hybrid of numerous languages, the result is a product that is as syntactically clean (if not cleaner) as Java, is about as simple as VB, and provides just about as much power and flexibility as C++. Here is a partial list of core C# features that are found in all versions of the language:

· No pointers required! C# programs typically have no need for direct pointer manipulation (although you are free to drop down to that level if absolutely necessary, as shown in Chapter 11).

· Automatic memory management through garbage collection. Given this, C# does not support a delete keyword.

· Formal syntactic constructs for classes, interfaces, structures, enumerations, and delegates.

· The C++-like ability to overload operators for a custom type, without the complexity (e.g., making sure to “return *this to allow chaining” is not your problem).

· Support for attribute-based programming. This brand of development allows you to annotate types and their members to further qualify their behavior. For example, if you mark a method with the [Obsolete] attribute, programmers will see your custom warning message print out if they attempt to make use of the decorated member.

With the release of .NET 2.0 (circa 2005), the C# programming language was updated to support numerous new bells and whistles, most notability the following:

· The ability to build generic types and generic members. Using generics, you are able to build efficient and type-safe code that defines numerous placeholders specified at the time you interact with the generic item

· Support for anonymous methods, which allow you to supply an inline function anywhere a delegate type is required

· The ability to define a single type across multiple code files (or if necessary, as an in-memory representation) using the partial keyword

.NET 3.5 (released circa 2008) added even more functionality to the C# programming language, including the following features:

· Support for strongly typed queries (e.g., LINQ) used to interact with various forms of data. You will first encounter LINQ in Chapter 12.

· Support for anonymous types that allow you to model the structure of a type (rather than its behavior) on the fly in code.

· The ability to extend the functionality of an existing type (without subclassing) using extension methods.

· Inclusion of a lambda operator (=>), which even further simplifies working with .NET delegate types.

· A new object initialization syntax, which allows you to set property values at the time of object creation.

.NET 4.0 (released in 2010) updated C# yet again with a handful of features.

· Support for optional method parameters, as well as named method arguments.

· Support for dynamic lookup of members at runtime via the dynamic keyword. As you will see in Chapter 18, this provides a unified approach to invoking members on the fly, regardless of which framework the member implemented (COM, IronRuby, IronPython, or via .NET reflection services).

· Working with generic types is much more intuitive, given that you can easily map generic data to and from general System.Object collections via covariance and contravariance.

With the release of .NET 4.5, C# received a pair of new keywords (async and await), which greatly simplify multithreaded and asynchronous programming. If you have worked with previous versions of C#, you might recall that calling methods via secondary threads required a fair amount of cryptic code and the use of various .NET namespaces. Given that C# now supports language keywords that handle this complexity for you, the process of calling methods asynchronously is almost as easy as calling a method in a synchronous manner. Chapter 19 will cover these topics in detail.

This brings us to the current version of C# and .NET 4.6, which introduces a number of minor features that help streamline your codebase. You will see a number of details as you go through this text; however, here is a quick rundown of some of the new features found in C#:

· Inline initialization for automatic properties as well as support for read-only automatic properties

· Single-line method implementations using the C# lambda operator

· Support of “static imports” to provide direct access to static members within a namespace

· A null conditional operator, which helps check for null parameters in a method implementation

· A new string formatting syntax termed string interpolation

· The ability to filter exceptions using the new when keyword

Managed vs. Unmanaged Code

It is important to note that the C# language can be used only to build software that is hosted under the.NET runtime (you could never use C# to build a native COM server or an unmanaged C/C++-style application). Officially speaking, the term used to describe the code targeting the .NET runtime is managed code. The binary unit that contains the managed code is termed an assembly (more details on assemblies in just a bit). Conversely, code that cannot be directly hosted by the .NET runtime is termed unmanaged code.

As mentioned previously (and detailed later in this chapter and the next), the .NET platform can run on a variety of operating systems. Thus, it is quite possible to build a C# application on a Windows machine using Visual Studio and run the program on a Mac OS X machine using the Mono .NET runtime. As well, you could build a C# application on Linux using Xamarin Studio (see Chapter 2) and run the program on Windows, Mac, and so on. To be sure, the notion of a managed environment makes it possible to build, deploy, and run .NET programs on a wide variety of target machines.

Additional .NET-Aware Programming Languages

Understand that C# is not the only language that can be used to build .NET applications. Out of the box, Visual Studio provides you with five managed languages, specifically, C#, Visual Basic, C++/CLI, JavaScript, and F#.

Image Note F# is a .NET language based on the syntax of functional languages. While F# can be used as a purely functional language, it also has support for OOP constructs and the .NET base class libraries. If you are interested in learning more about this managed language, navigate online to the official F# home page, http://msdn.microsoft.com/fsharp.

In addition to the managed languages provided by Microsoft, there are .NET compilers for Smalltalk, Ruby, Python, COBOL, and Pascal (to name a few). Although this book focuses almost exclusively on C#, you might want to consult the following Wikipedia page, which lists a large number of programming languages that target the .NET framework:

https://en.wikipedia.org/wiki/List_of_CLI_languages

While I assume you are primarily interested in building .NET programs using the syntax of C#, I encourage you to visit this site, as you are sure to find many .NET languages worth investigating at your leisure (LISP.NET, anyone?).

Life in a Multilanguage World

As developers first come to understand the language-agnostic nature of .NET, numerous questions arise. The most prevalent of these questions would have to be, “If all .NET languages compile down to managed code, why do we need more than one language/compiler?”

There are a number of ways to answer this question. First, we programmers are a very particular lot when it comes to our choice of programming language. Some of us prefer languages full of semicolons and curly brackets with as few language keywords as possible. Others enjoy a language that offers more human-readable syntactic tokens (such as Visual Basic). Still others might want to leverage their mainframe skills while moving to the .NET platform (via the COBOL .NET compiler).

Now, be honest. If Microsoft were to build a single “official” .NET language derived from the BASIC family of languages, can you really say all programmers would be happy with this choice? Or, if the only “official” .NET language was based on Fortran syntax, imagine all the folks out there who would ignore .NET altogether. Because the .NET runtime couldn’t care less which language was used to build a block of managed code, .NET programmers can stay true to their syntactic preferences and share the compiled code among teammates, departments, and external organizations (regardless of which .NET language others choose to use).

Another excellent byproduct of integrating various .NET languages into a single, unified software solution is the simple fact that all programming languages have their own sets of strengths and weaknesses. For example, some programming languages offer excellent intrinsic support for advanced mathematical processing. Others offer superior support for financial calculations, logical calculations, interaction with mainframe computers, and so forth. When you take the strengths of a particular programming language and then incorporate the benefits provided by the .NET platform, everybody wins.

Of course, in reality the chances are quite good that you will spend much of your time building software using your .NET language of choice. However, once you master the syntax of one .NET language, it is easy to learn another. This is also quite beneficial, especially to the software consultants of the world. If your language of choice happens to be C# but you are placed at a client site that has committed to Visual Basic, you are still able to leverage the functionality of the .NET Framework, and you should be able to understand the overall structure of the code base with minimal fuss and bother.

An Overview of .NET Assemblies

Regardless of which .NET language you choose to program with, understand that despite that .NET binaries take the same file extension as unmanaged Windows binaries (*.dll or *.exe), they have absolutely no internal similarities. Specifically, .NET binaries do not contain platform-specific instructions but rather platform-agnostic Intermediate Language (IL) and type metadata. Figure 1-2 shows the big picture of the story thus far.

image

Figure 1-2. All .NET-aware compilers emit IL instructions and metadata

Image Note There is one point to be made regarding the abbreviation “IL.” IL is also known as Microsoft Intermediate Language (MSIL) or alternatively as the Common Intermediate Language (CIL). Thus, as you read the .NET literature, understand that IL, MSIL, and CIL are all describing essentially the same concept. In this text, I will use the abbreviation CIL to refer to this low-level instruction set.

When a *.dll or *.exe has been created using a .NET-aware compiler, the binary blob is termed an assembly. You will examine numerous details of .NET assemblies in Chapter 14. However, to facilitate the current discussion, you do need to understand some basic properties of this new file format.

As mentioned, an assembly contains CIL code, which is conceptually similar to Java bytecode in that it is not compiled to platform-specific instructions until absolutely necessary. Typically, “absolutely necessary” is the point at which a block of CIL instructions (such as a method implementation) is referenced for use by the .NET runtime.

In addition to CIL instructions, assemblies also contain metadata that describes in vivid detail the characteristics of every “type” within the binary. For example, if you have a class named SportsCar, the type metadata describes details such as SportsCar’s base class, which interfaces are implemented by SportsCar (if any), as well as a full description of each member supported by the SportsCar type. .NET metadata is always present within an assembly and is automatically generated by a .NET-aware language compiler.

Finally, in addition to CIL and type metadata, assemblies themselves are also described using metadata, which is officially termed a manifest. The manifest contains information about the current version of the assembly, culture information (used for localizing string and image resources), and a list of all externally referenced assemblies that are required for proper execution. You’ll examine various tools that can be used to examine an assembly’s types, metadata, and manifest information over the course of the next few chapters.

The Role of the Common Intermediate Language

Let’s examine CIL code, type metadata, and the assembly manifest in a bit more detail. CIL is a language that sits above any particular platform-specific instruction set. For example, the following C# code models a trivial calculator. Don’t concern yourself with the exact syntax for now, but do notice the format of the Add() method in the Calc class.

// Calc.cs
using System;
namespace CalculatorExample
{
// This class contains the app’s entry point.
class Program
{
static void Main()
{
Calc c = new Calc();
int ans = c.Add(10, 84);
Console.WriteLine("10 + 84 is {0}.", ans);
// Wait for user to press the Enter key before shutting down.
Console.ReadLine();
}
}

// The C# calculator.
class Calc
{
public int Add(int x, int y)
{ return x + y; }
}
}

After you compile this code file using the C# compiler (csc.exe), you end up with a single-file *.exe assembly that contains a manifest, CIL instructions, and metadata describing each aspect of the Calc and Program classes.

Image Note Chapter 2 examines how to use graphical IDEs (such as Visual Studio Community Edition) to compile your code files.

For example, if you were to open this assembly using ildasm.exe (examined a little later in this chapter), you would find that the Add() method is represented using CIL such as the following:

.method public hidebysig instance int32 Add(int32 x,
int32 y) cil managed
{
// Code size 9 (0x9)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add
IL_0004: stloc.0
IL_0005: br.s IL_0007
IL_0007: ldloc.0
IL_0008: ret
} // end of method Calc::Add

Don’t worry if you are unable to make heads or tails of the resulting CIL for this method—Chapter 18 will describe the basics of the CIL programming language. The point to concentrate on is that the C# compiler emits CIL, not platform-specific instructions.

Now, recall that this is true of all .NET-aware compilers. To illustrate, assume you created this same application using Visual Basic, rather than C#.

’ Calc.vb
Imports System

Namespace CalculatorExample
’ A VB "Module" is a class that contains only
’ static members.
Module Program
Sub Main()
Dim c As New Calc
Dim ans As Integer = c.Add(10, 84)
Console.WriteLine("10 + 84 is {0}.", ans)
Console.ReadLine()
End Sub
End Module

Class Calc
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
Return x + y
End Function
End Class
End Namespace

If you examine the CIL for the Add() method, you find similar instructions (slightly tweaked by the Visual Basic compiler, vbc.exe).

.method public instance int32 Add(int32 x,
int32 y) cil managed
{
// Code size 8 (0x8)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add.ovf
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // end of method Calc::Add

Image Source Code The Calc.cs and Calc.vb code files are included in the Chapter 1 subdirectory.

Benefits of CIL

At this point, you might be wondering exactly what is gained by compiling source code into CIL rather than directly to a specific instruction set. One benefit is language integration. As you have already seen, each .NET-aware compiler produces nearly identical CIL instructions. Therefore, all languages are able to interact within a well-defined binary arena.

Furthermore, given that CIL is platform-agnostic, the .NET Framework itself is platform-agnostic, providing the same benefits Java developers have grown accustomed to (e.g., a single code base running on numerous operating systems). In fact, there is an international standard for the C# language, and a large subset of the .NET platform and implementations already exists for many non-Windows operating systems (more details at the conclusion of this chapter).

Compiling CIL to Platform-Specific Instructions

Because assemblies contain CIL instructions rather than platform-specific instructions, CIL code must be compiled on the fly before use. The entity that compiles CIL code into meaningful CPU instructions is a JIT compiler, which sometimes goes by the friendly name of Jitter. The .NET runtime environment leverages a JIT compiler for each CPU targeting the runtime, each optimized for the underlying platform.

For example, if you are building a .NET application to be deployed to a handheld device (such as a Windows mobile device), the corresponding Jitter is well equipped to run within a low-memory environment. On the other hand, if you are deploying your assembly to a back-end company server (where memory is seldom an issue), the Jitter will be optimized to function in a high-memory environment. In this way, developers can write a single body of code that can be efficiently JIT compiled and executed on machines with different architectures.

Furthermore, as a given Jitter compiles CIL instructions into corresponding machine code, it will cache the results in memory in a manner suited to the target operating system. In this way, if a call is made to a method named PrintDocument(), the CIL instructions are compiled into platform-specific instructions on the first invocation and retained in memory for later use. Therefore, the next time PrintDocument() is called, there is no need to recompile the CIL.

Image Note It is also possible to perform a “pre-JIT” of an assembly when installing your application using the ngen.exe command-line tool that ships with the .NET Framework SDK. Doing so can improve startup time for graphically intensive applications.

The Role of .NET Type Metadata

In addition to CIL instructions, a .NET assembly contains full, complete, and accurate metadata, which describes every type (e.g., class, structure, enumeration) defined in the binary, as well as the members of each type (e.g., properties, methods, events). Thankfully, it is always the job of the compiler (not the programmer) to emit the latest and greatest type metadata. Because .NET metadata is so wickedly meticulous, assemblies are completely self-describing entities.

To illustrate the format of .NET type metadata, let’s take a look at the metadata that has been generated for the Add() method of the C# Calc class you examined previously (the metadata generated for the Visual Basic version of the Add() method is similar; again, more on the use ofildasm in just a bit).

TypeDef #2 (02000003)
-------------------------------------------------------
TypDefName: CalculatorExample.Calc (02000003)
Flags : [NotPublic] [AutoLayout] [Class]
[AnsiClass] [BeforeFieldInit] (00100001)
Extends : 01000001 [TypeRef] System.Object
Method #1 (06000003)
-------------------------------------------------------
MethodName: Add (06000003)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x00002090
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: I4
2 Arguments
Argument #1: I4
Argument #2: I4
2 Parameters
(1) ParamToken : (08000001) Name : x flags: [none] (00000000)
(2) ParamToken : (08000002) Name : y flags: [none] (00000000)

Metadata is used by numerous aspects of the .NET runtime environment, as well as by various development tools. For example, the IntelliSense feature provided by tools such as Visual Studio is made possible by reading an assembly’s metadata at design time. Metadata is also used by various object- browsing utilities, debugging tools, and the C# compiler itself. To be sure, metadata is the backbone of numerous .NET technologies including Windows Communication Foundation (WCF), reflection, late binding, and object serialization. Chapter 15 will formalize the role of .NET metadata.

The Role of the Assembly Manifest

Last but not least, remember that a .NET assembly also contains metadata that describes the assembly itself (technically termed a manifest). Among other details, the manifest documents all external assemblies required by the current assembly to function correctly, the assembly’s version number, copyright information, and so forth. Like type metadata, it is always the job of the compiler to generate the assembly’s manifest. Here are some relevant details of the manifest generated when compiling the Calc.cs code file shown earlier in this chapter (assume you instructed the compiler to name your assembly Calc.exe):

.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly Calc
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module Calc.exe
.imagebase 0x00400000
.subsystem 0x00000003
.file alignment 0x00000200
.corflags 0x00000001

In a nutshell, this manifest documents the set of external assemblies required by Calc.exe (via the .assembly extern directive) as well as various characteristics of the assembly itself (e.g., version number, module name). Chapter 14 will examine the usefulness of manifest data in much more detail.

Understanding the Common Type System

A given assembly may contain any number of distinct types. In the world of .NET, type is simply a general term used to refer to a member from the set {class, interface, structure, enumeration, delegate}. When you build solutions using a .NET-aware language, you will most likely interact with many of these types. For example, your assembly might define a single class that implements some number of interfaces. Perhaps one of the interface methods takes an enumeration type as an input parameter and returns a structure to the caller.

Recall that the CTS is a formal specification that documents how types must be defined in order to be hosted by the CLR. Typically, the only individuals who are deeply concerned with the inner workings of the CTS are those building tools and/or compilers that target the .NET platform. It is important, however, for all .NET programmers to learn about how to work with the five types defined by the CTS in their language of choice. The following is a brief overview.

CTS Class Types

Every .NET-aware language supports, at the least, the notion of a class type, which is the cornerstone of object-oriented programming (OOP). A class may be composed of any number of members (such as constructors, properties, methods, and events) and data points (fields). In C#, classes are declared using the class keyword, like so:

// A C# class type with 1 method.
class Calc
{
public int Add(int x, int y)
{
return x + y;
}
}

Chapter 5 will begin your formal examination of building class types with C#; however, Table 1-1 documents a number of characteristics pertaining to class types.

Table 1-1. CTS Class Characteristics

Class Characteristic

Meaning in Life

Is the class sealed?

Sealed classes cannot function as a base class to other classes.

Does the class implement any interfaces?

An interface is a collection of abstract members that provide a contract between the object and object user. The CTS allows a class to implement any number of interfaces.

Is the class abstract or concrete?

Abstract classes cannot be directly instantiated but are intended to define common behaviors for derived types. Concrete classes can be instantiated directly.

What is the visibility of this class?

Each class must be configured with a visibility keyword such as public or internal. Basically, this controls whether the class may be used by external assemblies or only from within the defining assembly.

CTS Interface Types

Interfaces are nothing more than a named collection of abstract member definitions, which may be supported (i.e., implemented) by a given class or structure. In C#, interface types are defined using the interface keyword. By convention, all .NET interfaces begin with a capital letter I, as in the following example:

// A C# interface type is usually
// declared as public, to allow types in other
// assemblies to implement their behavior.
public interface IDraw
{
void Draw();
}

On their own, interfaces are of little use. However, when a class or structure implements a given interface in its unique way, you are able to request access to the supplied functionality using an interface reference in a polymorphic manner. Interface-based programming will be fully explored in Chapter 8.

CTS Structure Types

The concept of a structure is also formalized under the CTS. If you have a C background, you should be pleased to know that these user-defined types (UDTs) have survived in the world of .NET (although they behave a bit differently under the hood). Simply put, a structure can be thought of as a lightweight class type having value-based semantics. For more details on the subtleties of structures, see Chapter 4. Typically, structures are best suited for modeling geometric and mathematical data and are created in C# using the struct keyword, as follows:

// A C# structure type.
struct Point
{
// Structures can contain fields.
public int xPos, yPos;

// Structures can contain parameterized constructors.
public Point(int x, int y)
{ xPos = x; yPos = y;}

// Structures may define methods.
public void PrintPosition()
{
Console.WriteLine("({0}, {1})", xPos, yPos);
}
}

CTS Enumeration Types

Enumerations are a handy programming construct that allow you to group name-value pairs. For example, assume you are creating a video game application that allows the player to select one of three character categories (Wizard, Fighter, or Thief). Rather than keeping track of simple numerical values to represent each possibility, you could build a strongly typed enumeration using the enum keyword.

// A C# enumeration type.
enum CharacterType
{
Wizard = 100,
Fighter = 200,
Thief = 300
}

By default, the storage used to hold each item is a 32-bit integer; however, it is possible to alter this storage slot if need be (e.g., when programming for a low-memory device such as a mobile device). Also, the CTS demands that enumerated types derive from a common base class,System.Enum. As you will see in Chapter 4, this base class defines a number of interesting members that allow you to extract, manipulate, and transform the underlying name-value pairs programmatically.

CTS Delegate Types

Delegates are the .NET equivalent of a type-safe, C-style function pointer. The key difference is that a .NET delegate is a class that derives from System.MulticastDelegate, rather than a simple pointer to a raw memory address. In C#, delegates are declared using the delegatekeyword.

// This C# delegate type can "point to" any method
// returning an int and taking two ints as input.
delegate int BinaryOp(int x, int y);

Delegates are critical when you want to provide a way for one object to forward a call to another object and provide the foundation for the .NET event architecture. As you will see in Chapters 10 and 19, delegates have intrinsic support for multicasting (i.e., forwarding a request to multiple recipients) and asynchronous method invocations (i.e., invoking the method on a secondary thread).

CTS Type Members

Now that you have previewed each of the types formalized by the CTS, realize that most types take any number of members. Formally speaking, a type member is constrained by the set {constructor, finalizer, static constructor, nested type, operator, method, property, indexer, field, read-only field, constant, event}.

The CTS defines various adornments that may be associated with a given member. For example, each member has a given visibility trait (e.g., public, private, protected). Some members may be declared as abstract (to enforce a polymorphic behavior on derived types) as well as virtual (to define a canned, but overridable, implementation). Also, most members may be configured as static (bound at the class level) or instance (bound at the object level). The creation of type members is examined over the course of the next several chapters.

Image Note As described in Chapter 9, the C# language also supports the creation of generic types and generic members.

Intrinsic CTS Data Types

The final aspect of the CTS to be aware of for the time being is that it establishes a well-defined set of fundamental data types. Although a given language typically has a unique keyword used to declare a fundamental data type, all .NET language keywords ultimately resolve to the same CTS type defined in an assembly named mscorlib.dll. Consider Table 1-2, which documents how key CTS data types are expressed in various .NET languages.

Table 1-2. The Intrinsic CTS Data Types

Table1-2

Given that the unique keywords of a managed language are simply shorthand notations for a real type in the System namespace, you no longer have to worry about overflow/underflow conditions for numerical data or how strings and Booleans are internally represented across different languages. Consider the following code snippets, which define 32-bit numerical variables in C# and Visual Basic, using language keywords as well as the formal CTS data type:

// Define some "ints" in C#.
int i = 0;
System.Int32 j = 0;

’ Define some "ints" in VB.
Dim i As Integer = 0
Dim j As System.Int32 = 0

Understanding the Common Language Specification

As you are aware, different languages express the same programming constructs in unique, language-specific terms. For example, in C# you denote string concatenation using the plus operator (+), while in VB you typically make use of the ampersand (&). Even when two distinct languages express the same programmatic idiom (e.g., a function with no return value), the chances are good that the syntax will appear quite different on the surface.

// C# method returning nothing.
public void MyMethod()
{
// Some interesting code...
}

’ VB method returning nothing.
Public Sub MyMethod()
’ Some interesting code...
End Sub

As you have already seen, these minor syntactic variations are inconsequential in the eyes of the .NET runtime, given that the respective compilers (csc.exe or vbc.exe, in this case) emit a similar set of CIL instructions. However, languages can also differ with regard to their overall level of functionality. For example, a .NET language might or might not have a keyword to represent unsigned data and might or might not support pointer types. Given these possible variations, it would be ideal to have a baseline to which all .NET-aware languages are expected to conform.

The CLS is a set of rules that describe in vivid detail the minimal and complete set of features a given .NET-aware compiler must support to produce code that can be hosted by the CLR, while at the same time be accessed in a uniform manner by all languages that target the .NET platform. In many ways, the CLS can be viewed as a subset of the full functionality defined by the CTS.

The CLS is ultimately a set of rules that compiler builders must conform to if they intend their products to function seamlessly within the .NET universe. Each rule is assigned a simple name (e.g., CLS Rule 6) and describes how this rule affects those who build the compilers as well as those who (in some way) interact with them. The crème de la crème of the CLS is Rule 1.

· Rule 1: CLS rules apply only to those parts of a type that are exposed outside the defining assembly.

Given this rule, you can (correctly) infer that the remaining rules of the CLS do not apply to the logic used to build the inner workings of a .NET type. The only aspects of a type that must conform to the CLS are the member definitions themselves (i.e., naming conventions, parameters, and return types). The implementation logic for a member may use any number of non-CLS techniques, as the outside world won’t know the difference.

To illustrate, the following C# Add() method is not CLS compliant, as the parameters and return values make use of unsigned data (which is not a requirement of the CLS):

class Calc
{
// Exposed unsigned data is not CLS compliant!
public ulong Add(ulong x, ulong y)
{
return x + y;
}
}

However, if you were to only make use of unsigned data internally in a method, as follows:

class Calc
{
public int Add(int x, int y)
{
// As this ulong variable is only used internally,
// we are still CLS compliant.
ulong temp = 0;
...
return x + y;
}
}

you have still conformed to the rules of the CLS and can rest assured that all .NET languages are able to invoke the Add() method.

Of course, in addition to Rule 1, the CLS defines numerous other rules. For example, the CLS describes how a given language must represent text strings, how enumerations should be represented internally (the base type used for storage), how to define static members, and so forth. Luckily, you don’t have to commit these rules to memory to be a proficient .NET developer. Again, by and large, an intimate understanding of the CTS and CLS specifications is typically of interest only to tool/compiler builders.

Ensuring CLS Compliance

As you will see over the course of this book, C# does define a number of programming constructs that are not CLS compliant. The good news, however, is that you can instruct the C# compiler to check your code for CLS compliance using a single .NET attribute.

// Tell the C# compiler to check for CLS compliance.
[assembly: CLSCompliant(true)]

Chapter 15 dives into the details of attribute-based programming. Until then, simply understand that the [CLSCompliant] attribute will instruct the C# compiler to check every line of code against the rules of the CLS. If any CLS violations are discovered, you receive a compiler error and a description of the offending code.

Understanding the Common Language Runtime

In addition to the CTS and CLS specifications, the final three-letter abbreviation (TLA) to contend with at the moment is the CLR. Programmatically speaking, the term runtime can be understood as a collection of services that are required to execute a given compiled unit of code. For example, when Java developers deploy software to a new computer, they need to ensure the machine has been installed with the Java Virtual Machine (JVM) in order to run their software.

The .NET platform offers yet another runtime system. The key difference between the .NET runtime and the various other runtimes I just mentioned is that the .NET runtime provides a single, well-defined runtime layer that is shared by all languages and platforms that are .NET-aware.

The crux of the CLR is physically represented by a library named mscoree.dll (aka the Common Object Runtime Execution Engine). When an assembly is referenced for use, mscoree.dll is loaded automatically, which in turn loads the required assembly into memory. The runtime engine is responsible for a number of tasks. First, it is the agent in charge of resolving the location of an assembly and finding the requested type within the binary by reading the contained metadata. The CLR then lays out the type in memory, compiles the associated CIL into platform-specific instructions, performs any necessary security checks, and then executes the code in question.

In addition to loading your custom assemblies and creating your custom types, the CLR will also interact with the types contained within the .NET base class libraries when required. Although the entire base class library has been broken into a number of discrete assemblies, the key assembly is mscorlib.dll, which contains a large number of core types that encapsulate a wide variety of common programming tasks, as well as the core data types used by all .NET languages. When you build .NET solutions, you automatically have access to this particular assembly.

Figure 1-3 illustrates the high-level workflow that takes place between your source code (which is making use of base class library types), a given .NET compiler, and the .NET execution engine.

image

Figure 1-3. mscoree.dll in action

The Assembly/Namespace/Type Distinction

Each of us understands the importance of code libraries. The point of framework libraries is to give developers a well-defined set of existing code to leverage in their applications. However, the C# language does not come with a language-specific code library. Rather, C# developers leverage the language- neutral .NET libraries. To keep all the types within the base class libraries well organized, the .NET platform makes extensive use of the namespace concept.

A namespace is a grouping of semantically related types contained in an assembly or possibly spread across multiple related assemblies. For example, the System.IO namespace contains file I/O-related types, the System.Data namespace defines basic database types, and so on. It is important to point out that a single assembly (such as mscorlib.dll) can contain any number of namespaces, each of which can contain any number of types.

To clarify, Figure 1-4 shows the Visual Studio Object Browser utility (which can be found under the View menu). This tool allows you to examine the assemblies referenced by your current project, the namespaces within a particular assembly, the types within a given namespace, and the members of a specific type. Note that the mscorlib.dll assembly contains many different namespaces (such as System.IO), each with its own semantically related types (e.g., BinaryReader).

image

Figure 1-4. A single assembly can have any number of namespaces, and namespaces can have any number of types

The key difference between this approach and a language-specific library is that any language targeting the .NET runtime uses the same namespaces and same types. For example, the following three programs all illustrate the ubiquitous “Hello World” application, written in C#, VB, and C++/CLI:

// Hello world in C#.
using System;

public class MyApp
{
static void Main()
{
Console.WriteLine("Hi from C#");
}
}

’ Hello world in VB.
Imports System
Public Module MyApp
Sub Main()
Console.WriteLine("Hi from VB")
End Sub
End Module

// Hello world in C++/CLI.
#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hi from C++/CLI");
return 0;
}

Notice that each language is using the Console class defined in the System namespace. Beyond some obvious syntactic variations, these three applications look and feel very much alike, both physically and logically.

Clearly, one you are comfortable with your .NET programming language of choice, your next goal as a .NET developer is to get to know the wealth of types defined in the (numerous) .NET namespaces. The most fundamental namespace to get your hands around initially is namedSystem. This namespace provides a core body of types that you will need to leverage time and again as a .NET developer. In fact, you cannot build any sort of functional C# application without at least making a reference to the System namespace, as the core data types (e.g.,System.Int32, System.String) are defined here. Table 1-3 offers a rundown of some (but certainly not all) of the .NET namespaces grouped by related functionality.

The Role of the Microsoft Root Namespace

I’m sure you noticed while reading over the listings in Table 1-3 that System is the root namespace for a majority of nested namespaces (e.g., System.IO, System.Data). As it turns out, however, the .NET base class library defines a number of topmost root namespaces beyondSystem, the most useful of which is named Microsoft.

Table 1-3. A Sampling of .NET Namespaces

.NET Namespace

Meaning in Life

System

Within System, you find numerous useful types dealing with intrinsic data, mathematical computations, random number generation, environment variables, and garbage collection, as well as a number of commonly used exceptions and attributes.

System.Collections
System.Collections.Generic

These namespaces define a number of stock container types, as well as base types and interfaces that allow you to build customized collections.

System.Data
System.Data.Common
System.Data.EntityClient
System.Data.SqlClient

These namespaces are used for interacting with relational databases using ADO.NET.

System.IO
System.IO.Compression
System.IO.Ports

These namespaces define numerous types used to work with file I/O, compression of data, and port manipulation.

System.Reflection
System.Reflection.Emit

These namespaces define types that support runtime type discovery as well as dynamic creation of types.

System.Runtime.InteropServices

This namespace provides facilities to allow .NET types to interact with unmanaged code (e.g., C-based DLLs and COM servers), and vice versa.

System.Drawing
System.Windows.Forms

These namespaces define types used to build desktop applications using .NET’s original UI toolkit (Windows Forms).

System.Windows
System.Windows.Controls
System.Windows.Shapes

The System.Windows namespace is the root for several namespaces that represent the Windows Presentation Foundation (WPF) UI toolkit.

System.Linq
System.Xml.Linq
System.Data.DataSetExtensions

These namespaces define types used when programming against the LINQ API.

System.Web

This is one of many namespaces that allow you to build ASP.NET web applications.

System.Web.Http

This is one of many namespaces that allow you to build RESTful web services.

System.ServiceModel

This is one of many namespaces used to build distributed applications using the Windows Communication Foundation API.

System.Workflow.Runtime
System.Workflow.Activities

These are two of many namespaces that define types used to build “workflow-enabled” applications using the Windows Workflow Foundation API.

System.Threading
System.Threading.Tasks

This namespace defines numerous types to build multithreaded applications that can distribute workloads across multiple CPUs.

System.Security

Security is an integrated aspect of the .NET universe. In the security-centric namespaces, you find numerous types dealing with permissions, cryptography, and so on.

System.Xml

The XML-centric namespaces contain numerous types used to interact with XML data.

Any namespace nested within Microsoft (e.g., Microsoft.CSharp, Microsoft.ManagementConsole, Microsoft.Win32) contains types that are used to interact with services unique to the Windows operating system. Given this point, you should not assume that these types could be used successfully on other .NET-enabled operating systems such as Mac OS X. For the most part, this text will not dig into the details of the Microsoft rooted namespaces, so be sure to consult the .NET Framework 4.6 SDK documentation if you are interested.

Image Note Chapter 2 will illustrate the use of the .NET Framework 4.6 SDK documentation, which provides details regarding every namespace, type, and member within the base class libraries.

Accessing a Namespace Programmatically

It is worth reiterating that a namespace is nothing more than a convenient way for us mere humans to logically understand and organize related types. Consider again the System namespace. From your perspective, you can assume that System.Console represents a class namedConsole that is contained within a namespace called System. However, in the eyes of the .NET runtime, this is not so. The runtime engine sees only a single class named System.Console.

In C#, the using keyword simplifies the process of referencing types defined in a particular namespace. Here is how it works. Let’s say you are interested in building a graphical desktop application using the WPF API. While learning the types each namespace contains takes study and experimentation, here are some possible candidates to reference in your program:

// Here are some possible namespaces used to build a WPF application.
using System; // General base class library types.
using System.Windows.Shapes; // Graphical rendering types.
using System.Windows.Controls; // Windows Forms GUI widget types.
using System.Data; // General data-centric types.
using System.Data.SqlClient; // MS SQL Server data-access types.

Once you have specified some number of namespaces (and set a reference to the assemblies that define them), you are free to create instances of the types they contain. For example, if you are interested in creating an instance of the Button class (defined in theSystem.Windows.Controls namespace), you can write the following:

// Explicitly list the namespaces used by this file.
using System;
using System.Windows.Controls;

class MyGUIBuilder
{
public void BuildUI()
{
// Create a button control.
Button btnOK = new Button();
...
}
}

Because your code file is importing the System.Windows.Controls namespace, the compiler is able to resolve the Button class as a member of this namespace. If you did not import the System.Windows.Controls namespace, you would be issued a compiler error. However, you are free to declare variables using a fully qualified name as well.

// Not listing System.Windows.Controls namespace!
using System;

class MyGUIBuilder
{
public void BuildUI()
{
// Using fully qualified name.
System.Windows.Controls.Button btnOK =
new System.Windows.Controls.Button();
...
}
}

While defining a type using the fully qualified name provides greater readability, I think you’d agree that the C# using keyword reduces keystrokes. In this text, I will avoid the use of fully qualified names (unless there is a definite ambiguity to be resolved) and opt for the simplified approach of the C# using keyword.

However, always remember that the using keyword is simply a shorthand notation for specifying a type’s fully qualified name, and either approach results in the same underlying CIL (given that CIL code always uses fully qualified names) and has no effect on performance or the size of the assembly.

Referencing External Assemblies

In addition to specifying a namespace via the C# using keyword, you need to tell the C# compiler the name of the assembly containing the actual CIL implementation for the referenced type. As mentioned, many core .NET namespaces are defined within mscorlib.dll. However, by way of example, the System.Drawing.Bitmap class is contained within a separate assembly named System.Drawing.dll. A vast majority of the .NET Framework assemblies are located under a specific directory termed the global assembly cache (GAC). On a Windows machine, this can be located by default under C:\Windows\Assembly\GAC, as shown in Figure 1-5.

image

Figure 1-5. Many .NET libraries reside in the GAC

Depending on the development tool you are using to build your .NET applications, you will have various ways to inform the compiler which assemblies you want to include during the compilation cycle. You’ll examine how to do so in Chapter 2, so I’ll hold off on the details for now.

Image Note As you will see in Chapter 14, a Windows OS has multiple locations where framework libraries can be installed; however, this is generally encapsulated from the developer. On a non-Windows machine (such as Mac OS X or Linux), the location of the GAC depends on the .NET distribution.

Exploring an Assembly Using ildasm.exe

If you are beginning to feel a tad overwhelmed at the thought of gaining mastery over every namespace in the .NET platform, just remember that what makes a namespace unique is that it contains types that are somehow semantically related. Therefore, if you have no need for a user interface beyond a simple console application, you can forget all about the desktop and web namespaces (among others). If you are building a painting application, the database namespaces are most likely of little concern. Like any new set of prefabricated code, you learn as you go.

The Intermediate Language Disassembler utility (ildasm.exe), which ships with the .NET Framework, allows you to load up any .NET assembly and investigate its contents, including the associated manifest, CIL code, and type metadata. This tool allows a programmer to dive deeply into how their C# code maps to CIL and ultimately helps one understand the inner workings of the .NET platform. While you never need to use ildasm.exe to become a proficient .NET programmer, I highly recommend you fire up this tool from time to time to better understand how your C# code maps to runtime concepts.

Image Note You can easily run ildasm.exe by opening a Visual Studio command prompt and typing ildasm followed by the Enter key.

After you launch ildasm.exe, proceed to the File image Open menu command and navigate to an assembly you would like to explore. By way of illustration, Figure 1-6 shows the Calc.exe assembly generated based on the Calc.cs file shown earlier in this chapter. ildasm.exepresents the structure of an assembly using a familiar tree-view format.

image

Figure 1-6. ildasm.exe allows you to see the CIL code, manifest, and metadata within a .NET assembly

Viewing CIL Code

In addition to showing the namespaces, types, and members contained in a given assembly, ildasm.exe allows you to view the CIL instructions for a given member. For example, if you were to double-click the Main() method of the Program class, a separate window would display the underlying CIL (see Figure 1-7).

image

Figure 1-7. Viewing the underlying CIL

Viewing Type Metadata

If you want to view the type metadata for the currently loaded assembly, press Ctrl+M. Figure 1-8 shows the metadata for the Calc.Add() method.

image

Figure 1-8. Viewing type metadata via ildasm.exe

Viewing Assembly Metadata (aka the Manifest)

Finally, if you are interested in viewing the contents of the assembly’s manifest (see Figure 1-9), simply double-click the MANIFEST icon in the main window of ildasm.

image

Figure 1-9. Viewing manifest data via ildasm.exe

To be sure, ildasm.exe has more options than shown here, and I will illustrate additional features of the tool where appropriate in the text.

The Platform-Independent Nature of .NET

Allow me to briefly comment on the platform-independent nature of the .NET platform. To the surprise of many developers, .NET applications can be developed and executed on non-Microsoft operating systems, including Mac OS X, various Linux distributions, Solaris, and iOS and Android mobile devices. To understand how this is possible, you need to come to terms with yet another abbreviation in the .NET universe: CLI (Common Language Infrastructure).

When Microsoft released the C# programming language and the .NET platform, it also crafted a set of formal documents that described the syntax and semantics of the C# and CIL languages, the .NET assembly format, core .NET namespaces, and the mechanics of the .NET runtime engine. These documents have been submitted to (and ratified by) Ecma International (www.ecma-international.org) as official international standards. The specifications of interest are as follows:

· ECMA-334: The C# Language Specification

· ECMA-335: The Common Language Infrastructure (CLI)

The importance of these documents becomes clear when you understand that they enable third parties to build distributions of the .NET platform for any number of operating systems and/or processors. ECMA-335 is the “meatier” of the two specifications, so much so that it has been broken into various partitions, including those shown in Table 1-4.

Table 1-4. Partitions of the CLI

Partitions of ECMA-335

Meaning in Life

Partition I: Concepts and Architecture

Describes the overall architecture of the CLI, including the rules of the CTS and CLS and the mechanics of the .NET runtime engine.

Partition II: Metadata Definition and Semantics

Describes the details of .NET metadata and the assembly format.

Partition III: CIL Instruction Set

Describes the syntax and semantics of CIL code.

Partition IV: Profiles and Libraries

Gives a high-level overview of the minimal and complete class libraries that must be supported by a .NET distribution.

Partition V: Binary Formats

Describes a standard way to interchange debugging information between CLI producers and consumers.

Partition VI: Annexes

Provides a collection of odds-and-ends details such as class library design guidelines and the implementation details of a CIL compiler.

Be aware that Partition IV (Profiles and Libraries) defines only a minimal set of namespaces that represent the core services expected by a CLI distribution (e.g., collections, console I/O, file I/O, threading, reflection, network access, core security needs, XML data manipulation). The CLI does not define namespaces that facilitate web development (ASP.NET), database access (ADO.NET), or desktop graphical user interface (GUI) application development (Windows Presentation Foundation or Windows Forms).

The good news, however, is that the alternative .NET distribution (termed Mono) extends the CLI libraries with Microsoft-compatible equivalents of ASP.NET implementations, ADO.NET implementations, and various desktop GUI implementations to provide full-featured, production-level development platforms. To date, there are two major implementations of the CLI beyond Microsoft’s Windows-specific .NET platform. See Table 1-5.

Table 1-5. Open Source .NET Distributions

Distribution

Meaning in Life

The Mono project

The Mono project is an open source distribution of the CLI that targets various Linux distributions (e.g., SuSe, Fedora), Mac OS X, iOS devices (iPad, iPhone), Android devices, and (surprise!) Windows.

.NET Core 5

In addition to the Windows-Centric .NET Framework, Microsoft also supports a cross platform version of .NET, which focuses on the construction of code libraries and enterprise web applications.

The Mono Project

The Mono project is an excellent choice if you want to build .NET software that can run on a variety of operating systems. In addition to all the key .NET namespaces, Mono provides additional libraries to allow the construction of GUI-based desktop software, ASP.NET web applications, and software-targeting mobile devices (iPad, iPhone, and Android). You can download the Mono distribution from the following URL:

www.mono-project.com/

Out of the box, the Mono project consists of a number of command-line tools and all of the associated code libraries. However, as you will see in Chapter 2, there is a full-fledged graphical IDE typically used with Mono named Xamarin Studio. In fact, Microsoft Visual Studio projects can be loaded into Xamarin Studio projects, and vice versa. Again, you can find more information in Chapter 2, but you might want to check out the Xamarin web site for more details.

http://xamarin.com/

Image Note Appendix B in your code download folder provides an overview of the Mono platform.

Microsoft .NET Core

The other major cross-platform distribution of .NET comes from Microsoft Corporation. Beginning in 2014, Microsoft announced an open source version of its full-scale (Windows specific) .NET 4.6 Framework called .NET Core. The .NET Core distribution is not a complete carbon copy of the .NET 4.6 Framework. Rather, .NET Core focuses on the construction of ASP.NET web applications that can run on Linux, Mac OS X, and Windows. Thus, you can essentially consider .NET Core to be a subset of the full .NET Framework. You can find a good article that compares and contrasts the full .NET Framework to the .NET Core framework on the MSDN .NET Blog site. Here is a direct link (but if this changes, just do a web search for.NET Core is Open Source):

http://blogs.msdn.com/b/dotnet/archive/2014/11/12/net-core-is-open-source.aspx

As luck would have it, all the features of C#, as well as a number of key libraries, are included in .NET Core. Therefore, a majority of this book will map directly to this distribution. Recall, though, that .NET Core is focused on building web applications and does not provide implementations of desktop GUI APIs (such as WPF or Windows Forms). If you need to build cross-platform desktop GUI applications, the Mono project is the better choice.

It is also worth noting that Microsoft has also released a free, light-weight, and cross-platform code editor to help support development with .NET Core. This editor is simply named Visual Studio Code. While it is certainly not as full featured as Microsoft Visual Studio or Xamarin Studio, it is a useful tool to edit C# code in a cross-platform manner. While this text will not use Visual Studio Code, you might want to learn more at the following web site:

https://code.visualstudio.com/

Summary

The point of this chapter was to lay out the conceptual framework necessary for the remainder of this book. I began by examining a number of limitations and complexities found within the technologies prior to .NET and followed up with an overview of how .NET and C# attempt to simplify the current state of affairs.

.NET basically boils down to a runtime execution engine (mscoree.dll) and base class library (mscorlib.dll and associates). The Common Language Runtime (CLR) is able to host any .NET binary (aka assembly) that abides by the rules of managed code. As you have seen, assemblies contain CIL instructions (in addition to type metadata and the assembly manifest) that are compiled to platform-specific instructions using a just-in-time (JIT) compiler. In addition, you explored the role of the Common Language Specification (CLS) and Common Type System (CTS). This was followed by an examination of the ildasm.exe object browsing tool.

In the next chapter, you will take a tour of the common integrated development environments (IDEs) you can use when you build your C# programming projects. You will be happy to know that in this book, I will use completely free (and very feature rich) IDEs, so you can start exploring the .NET universe with no money down.