Skip to content

Commit

Permalink
Merge pull request #13 from dotnet/netprimerwork
Browse files Browse the repository at this point in the history
Adding content to .NET Primer
  • Loading branch information
Zlatko Knezevic committed Jun 16, 2015
2 parents 03506f3 + 57efb97 commit 187de49
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 183 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
docs/_build/
.idea/
*.swp

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
Expand Down
6 changes: 6 additions & 0 deletions docs/concepts/common-language-runtime.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. include:: /stub-topic.txt

|stub-icon| Common Language Runtime
===================================

.. include:: /stub-notice.txt
50 changes: 50 additions & 0 deletions docs/concepts/common-type-system.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Common Type System & Common Language Specification
==================================================

Author: `Zlatko Knezevic`_

Again, two terms that are freely used in the .NET world, they actually are crucial to understand how the .NET platform enables multi-language development and to understand how it works.

Common Type System
------------------

To start from the beggining, remember that the .NET platform is *language agnostic*. This doesn't just mean that a programmer can write her code in any language that can be compiled to IL. It also means that she needs to be able to interact with code written in other languages that are able to be used on the .NET platform.

In order to do this transparently, there has to be a common way to describe all supported types. This is what the Common Type System (CTS) is in charge of doing. It was made to do several things:

* Establish a framework for cross-language execution.
* Provide an object-oriented model to support implementing various languages on .NET platform.
* Define a set of rules that all languages must follow when it comes to working with types.
* Provide a library that contains the basic primitive types that are used in application development (i.e. ``Boolean``, ``Byte``, ``Char`` etc.)

CTS defines two main kinds of types that should be supported: reference and value types. Their names point to their definitions.

Reference types' objects are represented by a reference to the object's actual value; a reference here is similar to a pointer in C/C++. It simply refers to a memory location where the objects' values are. This has a profound impact on how these types are used. If you assign a reference type to a variable and then pass that variable into a method, for instance, any changes to the object will be reflected on the main object; there is no copying.

Value types are the opposite, where the objects are represented by their vaulues. If you assign a value type to a variable, you are essentially copying a value of the object.

CTS defines several categories of types, each with their specific semantics and usage:

* Classes
* Structures
* Enums
* Interfaces
* Delegates

CTS also defines all other properties of the types, such as access modifiers, what are valid type members, how inheritance and overloading works and so on. Unfortunately, going deep into any of those is beyond the scope of an introductory article such as this, but you can consult `More resources`_ section at the end for links to more in-depth content that covers these topics.

Common Language Specification
-----------------------------

To enable full interoperability scenarios, all objects that are created in code must rely on some commonality in the languages that are consuming them (are their *callers*). Since there are numerous different languages, .NET platform has specified those commonalities in something called the **Common Language Specification** (CLS). CLS defines a set of features that are needed by many common applications. It also provides a sort of recipe for any language that is implemented on top of .NET platform on what it needs to support.

CLS is a subset of the CTS. This means that all of the rules in the CTS also apply to the CLS, unless the CLS rules are more strict. If a component is built using only the rules in the CLS, that is, it exposes only the CLS features in its API, it is said to be **CLS-compliant**. For instance, the :doc:`<framework-librares>` are CLS-compliant precisely because they need to work across all of the languages that are supported on the .NET platform.

You can consult the documents in the `More Resources`_ section below to get an overview of all the features in the CLS.

More resources
--------------

* `Common Type System <https://msdn.microsoft.com/en-us/library/vstudio/zcx1eb1e(v=vs.100).aspx>`_
* `Common Language Specification <https://msdn.microsoft.com/en-us/library/vstudio/12a7a7h3(v=vs.100).aspx>`_

151 changes: 151 additions & 0 deletions docs/concepts/delegates-lambdas.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Delegates and lambdas
=====================

Delegates define a type, which specify a particular method signature. A
method (static or instance) that satisfies this signature can be
assigned to a variable of that type, then called directly (with the
appropriate arguments) or passed as an argument itself to another method
and then called. The following example demonstrates delegate use.

.. code-block:: c#
:linenos:
public class Program
{
public delegate string Reverse(string s);
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Reverse rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
* On line 4 we create a delegate type of a certain signature, in this case a method that takes a string parameter and then returns a string parameter.
* On line 6, we define the implementation of the delegate by providing a method that has the exact same signature.
* On line 13, the method is assigned to a type that conforms to the ``Reverse`` delegate.
* Finally, on line 15 we invoke the delegate passing a string to be reversed.

In order to streamline the development process, .NET includes a set of delegate types that programmers can reuse and not have to create new types. These are ``Func<>``, ``Action<>`` and ``Predicate<>``, and they can be used in various places throughout the .NET APIs without the need to define new delegate types. Of course, there are some differences between the three as you will see in their signatures which mostly have to do with the way they were meant to be used:

* ``Action<>`` is used when there is a need to perform an action using the arguments of the delegate.
* ``Func<>`` is used usually when you have a transformation on hand, that is, you need to transform the arguments of the delegate into a different result. Projections are a prime example of this.
* ``Predicate<>`` is used when you need to determine if the argument satisfies the condition of the delegate. It can also be written as a ``Func<T, bool>``.

We can now take our example above and rewrite it using the ``Func<>`` delegate instead of a custom type. The program will continue running exactly the same.

.. code-block:: c#
public class Program
{
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Func<string, string> rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
For this simple example, having a method defined outside of the Main() method seems a bit superfluous. It is because of this that .NET Framework 2.0 introduced the concept of **anonymous delegates**. With their support you are able to create "inline" delegates without having to specify any additional type or method. You simply inline the definition of the delegate where you need it.

For an example, we are going to switch it up and use our anonymous delegate to filter out a list of only even numbers and then print them to the console.

.. code-block:: c#
:linenos:
:emphasize-lines: 14-17
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(
delegate(int no)
{
return (no%2 == 0);
}
);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
Notice the highlighted lines. As you can see, the body of the delegate is just a set of expressions, as any other delegate. But instead of it being a separate definition, we've introduced it *ad hoc* in our call to the ``FindAll()`` method of the ``List<T>`` type.

However, even with this approach, there is still much code that we can throw away. This is where **lambda expressions** come into play.

Lambda expressions, or just "lambdas" for short, were introduced first in C# 3.0, as one of the core building blocks of Language Integrated Query (LINQ). They are just a more convenient syntax for using delegates. They declare a signature and a method body, but don't have an formal identity of their own, unless they are assigned to a delegate. Unlike delegates, they can
be directly assigned as the left-hand side of event registration or in various Linq clauses and methods.

Since a lambda expression is just another way of specifying a delegate, we should be able to rewrite the above sample to use a lambda expression instead of an anonymous delegate.

.. code-block:: c#
:linenos:
:emphasize-lines: 13
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(i => i % 2 == 0);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
If you take a look at the highlighted lines, you can see how a lambda expression looks like. Again, it is just a **very** convenient syntax for using delegates, so what happens under the covers is similar to what happens with the anonymous delegate.

Again, lambdas are just delegates, which means that they can be used as an event handler without any problems, as the following code snippet illustrates.

.. code-block:: c#
public MainWindow()
{
InitializeComponent();
Loaded += (o, e) =>
{
this.Title = "Loaded";
};
}
Further reading and resources
-----------------------------

* `Delegates <https://msdn.microsoft.com/en-us/library/ms173171.aspx>`_
* `Anonymous Functions <https://msdn.microsoft.com/en-us/library/bb882516.aspx>`_
* `Lambda expressions <https://msdn.microsoft.com/en-us/library/bb397687.aspx>`_
75 changes: 75 additions & 0 deletions docs/concepts/generics.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Generic Types (Generics) Overview
=================================
By `Kasey Uhlenhuth`_

We use generics all the time in C#, whether implicitly of explicitly. When you use LINQ in C#, did you ever notice that you are working with IEnumerable<T>? Or if you every saw an online sample of a "generic repository" for talking to databases using Entity Framework, did you see that most methods return IQueryable<T>? You may have wondered what the **T** is in these examples and why is it in there?

First introduced to the .NET Framework 2.0, generics involved changes to both the C# language and the Common Language Runtime (CLR). **Generics** are essentially a "code template" that allows developers to define `type-safe <https://msdn.microsoft.com/en-us/library/hbzz1a9a%28v=vs.110%29.aspx>`_ data structures without committing to an actual data type. For example, ``List<T>`` is a `Generic Collection <https://msdn.microsoft.com/en-us/library/System.Collections.Generic(v=vs.110).aspx>`_ that can be declared and used with any type: ``List<int>``, ``List<string>``, ``List<Person>``, etc.

So, what's the point? Why are generics useful? In order to understand this, we need to take a look at a specific class before and after adding generics. Let's look at the ``ArrayList``. In C# 1.0, the ``ArrayList`` elements were of type ``object``. This meant that any element that was added was silently converted into an ``object``; same thing happens on reading the elements from the list (this process is known as `boxing <https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx>`_ and unboxing respectively). Boxing and unboxing have an impact of performance. More than that, however, there is no way to tell at compile time what is the actual type of the data in the list. This makes for some fragile code. Generics solve this problem by providing additional information the type of data each instance of list will contain. Put simply, you can only add integers to ``List<int>`` and only add Persons to ``List<Person>``, etc.

Generics are also available at runtime, or **reified**. This means the
runtime knows what type of data structure you are using and can store it
in memory more efficiently.

Here is a small program that illustrates the efficiency of knowing the
data structure type at runtime:

.. code-block:: c#
:linenos:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List ListGeneric = new List { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
This program yields the following output:

.. code-block:: console
Generic Sort: System.Collections.Generic.List\`1[System.Int32] Time taken: 0.0789ms
Non-Generic Sort: System.Collections.ArrayList Time taken: 2.4324ms
The first thing you notice here is that sorting the generic list is
significantly faster than for the non-generic list. You might also
notice that the type for the generic list is distinct ([System.Int32])
whereas the type for the non-generic list is generalized. Because the
runtime knows the generic ``List<int>`` is of type int, it can store the
list elements in an underlying integer array in memory while the
non-generic ``ArrayList`` has to cast each list element as an object as
stored in an object array in memory. As shown through this example, the
extra castings take up time and slow down the list sort.

The last useful thing about the runtime knowing the type of your generic
is a better debugging experience. When you are debugging a generic in
C#, you know what type each element is in your data structure. Without
generics, you would have no idea what type each element was.

Further reading and resources
-----------------------------

* `An Introduction to C# Generics <https://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx>`_
* `C# Programming Guide - Generics <https://msdn.microsoft.com/en-us/library/512aeb7t.aspx>`_
32 changes: 32 additions & 0 deletions docs/concepts/managed-code.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
What is "managed code"?
=======================

When working with .NET Framework, you will often encounter the term "managed code". This document will explain what this term means and additional information around it.

To put it very simply, managed code is just that: code whose execution is managed by a runtime. In this case, the runtime in question is called the **Common Language Runtime** or CLR, regardless of the implementation (`Mono <http://www.mono-project.com/>`_ or .NET Framework or .NET Core). CLR is in charge of taking the managed code, compiling it into machine code and then executing it. On top of that, runtime provides several important services such as automatic memory management, security boundaries, type safety etc.

Contrast this to the way you would run a C/C++ program, also called "unmanaged code". In the unmanaged world, the programmer is in charge of pretty much everything. The actual program is, essentially, a binary that the operating system (OS) loads into memory and starts. Everything else, from memory management to security considerations are a burden of the programmer.

Managed code is written in one of the high-level languages that can be run on top of the .NET platform, such as C#, Visual Basic, F# and others. When you compile code written in those languages with their respective compiler, you don't get machine code. You get **Intermmidiate Language** code which the runtime then compiles and executes. C++ is the one exception to this rule, as it can also produce native, unmanaged binaries that run on Windows.

Intermediate Language & Execution
---------------------------------

What is "Intermediate Language" (or IL for short)? It is a product of compilation of code written in high-level .NET languages. Once you compile your code written in one of these languages, you will get a binary that is made out of IL. It is important to note that the IL is independent from any specific language that runs on top of the runtime; there is even a separate specification for it that you can read if you're so inclined.

Once you produce IL from your high-level code, you will most likely want to run it. This is where the CLR takes over and starts the process of **Just-In-Time** compiling, or **JIT-ing** your code from IL to machine code that can actually be ran on a CPU. In this way, the CLR knows exactly what your code is doing and can effectivelly *manage* it.

Umanaged code interoperability
------------------------------

Of course, the CLR allows passing the boundaries between managed and unmanaged world, and there is a lot of code that does that, even in the :doc:`Base Class Libraries <framework-libraries>`. This is called **interoperability** or just **interop** for short. These provisions would allow you to, for example, wrap up an unmanaged library and call into it. However, it is important to note that once you do this, when the code passes the boundaries of the runtime, the actual management of the execution is again in the hand of unmanged code, and thus falls under the same restrictions.

Similar to this, C# is one language that allows you to use unmanaged constructs such as pointers directly in code by utilizing what is known as **unsafe context** which designates a piece of code for which the execution is not managed by the CLR.

More resources
--------------

* `.NET Framework Conceptual Overview <https://msdn.microsoft.com/en-us/library/zw4w595w(v=vs.85).aspx>`_
* `Unsafe Code and Pointers <https://msdn.microsoft.com/en-us/library/t2yzs44b.aspx>`_
* `Interoperability (C# Programming guide) <https://msdn.microsoft.com/en-us/library/ms173184.aspx>`_

0 comments on commit 187de49

Please sign in to comment.