Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

[WIP] Add .NET Primer #725

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/README.md
Expand Up @@ -13,6 +13,11 @@ Product Instructions
- [Testing CoreCLR Changes](test-instructions.md)
- [Creating a Custom DNX](dnx-instructions.md)

.NET Primer
===========

- [.NET Primer](dotnet-primer.md)

Book of the Runtime
===================

Expand Down
26 changes: 26 additions & 0 deletions Documentation/assembly-format.md
@@ -0,0 +1,26 @@
.NET Assembly File Format
=========================

The .NET platform defines a binary file format - "assembly" - that is used to fully-describe and contain .NET programs. Assemblies are used for the programs themselves as well as any dependent libraries. A .NET program can be executed as one of more assemblies, with no other required artifacts, beyond the appropriate .NET runtime. Native dependencies, including operating system APIs, are a separate concern and are not contained within the .NET assembly format, although are sometimes described with this format (e.g. WinRT).

> Each CLI component carries the metadata for declarations, implementations, and references specific to that component. Therefore, the component-specific metadata is referred to as component metadata, and the resulting component is said to be self-describing -- from ECMA 335 I.9.1, Components and assemblies.

The format is fully specified and standardized as [ECMA 335](dotnet-standards.md). All .NET compilers and runtimes use this format. The presense of a documented and infrequently updated binary format has been a major benefit (arguably a requirement) for interoperatibility. The format was last updated in a substantive way in 2005 (.NET 2.0) to accomodate generics and processor architecture.

The format is CPU- and OS-agnostic. It has been used as part of .NET runtimes that target many chips and CPUs. While the format itself has Windows heritage, it is implementable on any operating system. It's arguably most significant choice for OS interopertability is that most values are stored in little-endian format. It doesn't have a specific affinity to machine pointer size (e.g. 32-bit, 64-bit).

The .NET assembly format is also very descriptive about the structure of a given program or library. It describes the internal components of an assembly, specifically: assembly references and types defined and their internal structure. Tools or APIs can read and process this information for display or to make programmatic decisions.

Format
======

The .NET binary format is based on the Windows [PE file](http://en.wikipedia.org/wiki/Portable_Executable) format. In fact, .NET class libraries are conformant Windows PEs, and appear on first glance to be Windows dynamic link libraries (DLLs) or application executables (EXEs). This is a very useful characteristic on Windows, where they can masquerade as native executable binaries and get some of the same treatment (e.g. OS load, PE tools).

![Assembly Headers](images/assembly-headers.png)

Assemblies headers from ECMA 335 II.25.1, Structure of the runtime file format.

Processing the Assemblies
=========================

It is possible to write tools or APIs to process assemblies. Assembly information enables making programmatic decisions at runtime, re-writing assemblies, providing API intellisense in an editor and generating documentation. [System.Reflection](https://msdn.microsoft.com/library/system.reflection.aspx) and [Mono.Cecil](http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/) are good examples of tools that are frequently used for this purpose.
50 changes: 50 additions & 0 deletions Documentation/class-libraries.md
@@ -0,0 +1,50 @@
.NET Class Libraries
====================

Class libraries are the [shared library](http://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries) concept for .NET. They enable you to componentize useful functionality into modules that can be used by multiple applications. They can also be used as a means of loading functionality that is not needed or not known at application startup. Class libraries are described using the [.NET Assembly file format](assembly-format.md).

There are three types of class libraries that you can use:

- **Platform-specific** class libraries have access to all the APIs in a given platform (e.g. .NET Framework, Xamarin iOS), but can only be used by apps and libraries that target that platform.
- **Portable** class libraries have access to a subset of APIs, and can be used by apps and libraries that target multiple platforms.
- **.NET Core** class libraries are a merger of the platform-specific and portable library concept into a single model that provides the best of both.

Platform-specific Class Libraries
=================================

Platform-specific libraries are bound to a single .NET platform (e.g. .NET Framework on Windows) and can therefore take significant dependencies on a known execution environment. Such an environment will expose a known set of APIs (.NET and OS APIs) and will maintain and expose expected state (e.g. Windows registry).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems confusing to me. The "known execution environment" is just a list of APIs that are available, but this paragraph makes it sound more complicated. I think it would be clearer to phrase the definitions for all the library types in terms of API surface instead of execution environment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more complicated than that. It's the combination of .NET APIs, native APIs, formats like Xaml and access to the registry. No?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my comment may have gone to the wrong line (or moved after a further change). I meant for it to apply to the Portable Class Libraries description. I think talking about a synthetic "known execution environment" confuses things there. (I did suggest changing all the descriptions, but I think the PCL one is the important one).

In terms of the things you mentioned, from a PCL perspective whether those things are available maps to whether the APIs are available or not. You can't access the registry because the registry APIs aren't portable. You can use WinRT Xaml in portable libraries when the right APIs area available.


Developers who create plaform specific libraries can fully exploit the underlying platform. The libraries will only ever run on that given platform, making platform checks or other forms of conditional code unnecessary (modulo single sourcing code for multiple platforms).

Platform-specific libraries have been the primary class library type for the .NET Framework. Even as other .NET platforms emerged, platform-specific libraries remained the dominant library type.

Portable Class Libraries
========================

Portable libraries are supported on multiple .NET platforms. They can still take dependencies on a known execution environment, however, the environment is a synthetic one that is generated by the intersection of a set of concrete .NET platforms. This means that exposed APIs and platform assumptions are a subset of what would be available to a platform-specific library.

You choose a platform configuration when you create a portable library. These are the set of platforms that you need to support (e.g. .NET Framework 4.5+, Windows Phone 8.0+). The more platforms you opt to support, the fewer APIs and fewer platform assumptions you can make, the lowest common denominator. This characteristic can be confusing at first, since people often think "more is better", but find that more supported platforms results in fewer available APIs.

Many library developers have switched from producing multiple platform-specific libraries from one source (using conditional compilation directives) to portable libraries. There are several techniques for accessing platform-specific functionality within portable libraries.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend listing or linking to descriptions of the techniques to access platform specific functionality from a PCL. A good overview of the techniques is here. Over the past year, the bait and switch approach has gained popularity and seems to be the most widely accepted technique for reusable libraries.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion. Fixed.


.NET Core Class Libraries
=========================

.NET Core libraries are a replacement of the platform-specific and portable libraries concepts. They are platform-specific in the sense that they expose all functionality from the underlying platform (no synthetic platforms or platform intersections). They are portable in the sense that they work on all supporting platforms.

.NET Core exposes a set of library _contracts_. .NET platforms must support each contract fully or not at all. Each platform, therefore, supports a set of .NET Core contracts. The corollary is that each .NET Core class library is supported on the platforms that support it's contract dependencies.

.NET Core contracts do not expose the entire functionality of the .NET Framework (nor is that a goal), however, they do expose many more APIs than Portable Class Libraries. More APIs will be added over time.

The following platforms support .NET Core class libraries:

- .NET Core 5
- ASP.NET 5
- .NET Framework 4.5+
- Windows Store Apps
- Windows Phone 8+

Mono Class Libraries
====================

Class libraries are supported on Mono, including the three types of libraries described above. Mono has often been seen (correctly) as a cross-platform implementation of the Microsoft .NET Framework. In part, this was because platform-specific .NET Framework libraries could run on the Mono runtime without modification or re-compilation. This characteristic was in place before the creation of portable class libraries, so was an obvious choice to enable binary portability between the .NET Framework and Mono (although it only worked in one direction).
173 changes: 173 additions & 0 deletions Documentation/dotnet-primer.md
@@ -0,0 +1,173 @@
.NET Primer
===========

.NET is a general purpose development platform. It can be used for any kind of app type or workload where general purpose solutions are used. It has several key features that are attractive to many developers, including automatic memory management and modern programming languages, that make it easier to efficiently build high-quality apps. .NET enables a high-level programming environment with many convenience features, while providing low-level access to native memory and APIs.

Multiple implementations of .NET are available, based on open [.NET Standards](dotnet-standards.md) that specify the fundamentals of the platform. They are separately optimized for different app types (e.g. desktop, mobile, gaming, cloud) and support many chips (e.g. x86/x64, ARM) and operating systems (e.g. Windows, Linux, iOS, Android, OS X). Open source is also an important part of the .NET ecosystem, with multiple .NET implementations and many libraries available under OSI-approved licenses.

Defining .NET Features
======================

There are a set of key features that together define .NET. Most of them are not unique on their own, but the particular aggregation of these features is what defines .NET as being distinct.

- [Automatic memory management](garbage-collection-overview.md)
- Verifiable code - type and memory safety
- Flexible native code compilation (enables JIT and AOT)
- Fast native interop and access to native memory
- Operating system agnostic
- Chip-agnostic (e.g. x86, ARM64) byte-code
- Language-agnostic (e.g. C#, F#, VB) runtime
- Flexible type system
- Expressive (imperative and functional) programming languages
- Programmable source code compiler (Roslyn)
- Large standard class library
- [.NET Class Libraries](class-libraries.md)
- [Assembly format](assembly-format.md)
- Package management

TODO: Ensure that this list is correct. Write and link to a chapter for each of these defining features.

.NET by Example
===============

.NET includes many features and capabilities that you will use on a daily basis for common tasks and needs. You can see these described below, with examples. Some of the same topics are discussed in more detail in documents linked above.

Memory Safety
-------------

One of the less obvious but quite far-reaching features that a garbage collector enables is memory safety. The invariant of memory safety is very simple: a program is memory safe if it accesses only memory that has been allocated (and not freed). Dangling pointers are always bugs, and tracking them down is often quite difficult.

The .NET runtime provides additional services, to complete the promise of memory safety, not naturally offered by a GC. It ensures that programs do not index off the end of an array or accessing a phantom field off the end of an object.

The following example will throw as a result of memory safety.

int[] numbers = new int[42];
int number = numbers[42]; // will throw (indexes are 0-based)

Type Safety
-----------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would make a better first section than "Automatic Memory Management", but that's just a personal opinion.


Objects are allocated in terms of types. The only operations allowed for a given object, and the memory it consumes, are those of its type. A `Dog` type may have `Jump` and `WagTail` methods, but not likely a `SumTotal` method. A program can only call the declared methods of a given type. All other calls will result in an exception.

.NET languages can be object-oriented, with hierarchies of base and derived classes. The .NET runtime will only allow object casts and calls that align with the object hierarchy.

Dog dog = Dog.AdoptDog();
Pet pet = (Pet)dog; // Dog derives from Pet
pet.ActCute();
Car car = (Car)dog; // will throw - no relationship between Car and Dog
object temp = (object)dog; // legal - a Dog is an object
car = (Car)temp; // will throw - the runtime isn't fooled
car.Accelerate() // the dog won't like this, nor will the program get this far

Type safety also guarantees the fidelity of accessor keywords (e.g. private, public, internal). This is particularly useful for non-public data that an implementation uses to manage its behavior.

Dog dog = Dog._nextDogToBeAdopted; // will throw - this is a private field

Generic Types
-------------

Generic types enable a kind of uncoordinated mix-ins of (largely) arbitrary types. They allow the creation of a kind of _compound type_. A generic type is considered to be _open_, as it requires a set of types (however many type parameters it exposes) to be provided in order to be _closed_. While static in nature, like the rest of the .NET type system, generics provide a kind of faux dynamism that developers find very useful.

The most commonly used generic type is `List<T>` and its sibling `Dictionary<K,V>`. You can use these types, and also create your own generic types. The following example demonstrates the use of these two built-in generic types.

List<string> strings = new List<string>();
strings.Add("string A");
strings.Add("string B");

Dictionary<int,List<string>> lists = new Dictionary<int,List<string>>();
lists.Add(DateTime.Now.Hour,strings); // new strings added every hour

Async Programming
-----------------

Async is a first-class concept within .NET, with async support in the runtime, the framework libraries and various .NET languages. Async is based off of the `Task` concept, which encapsulates a set of operations to be completed. Tasks are distinct from threads and may not rely on threads or require CPU time much at all, particularly for i/o-bound tasks.

TODO: Elaborate on Task concept.

C# includes special treatment for async, including the special keyword `await` for managing tasks. The following example demonstrates calling a web service as an async operation.

string url = "http://someUrl";
HttpClient client = new HttpClient();
string json = await client.GetStringAsync(url);

The call to `client.GetStringAsync(url)` does not block, but instead immediately yields by returning a `Task`. Computation resumes and the call returns the requested string when the network activity has completed.

Language Integrated Query (LINQ)
--------------------------------

.NET programs typically operate on some form of data. The data can be database-resident or in the form of objects (sometimes called POCOs - "Plain Old CLR Objects"). LINQ provides a language-integrated uniform query model over data, independent of the source. Linq providers bridge the gap between the uniform query model and the form of the data, such as SQL Server tables, XML documents, standard collections like List<T> and more.

The follow examples demonstrate various uses of LINQ to query different forms of data.

TODO: Examples.

Delegates and Lambdas
---------------------

Delegates are like C++ function pointers, but are type safe. They are a kind of disconnected method within the CLR type system. Regular methods are attached to a class and only directly callable through static or instance calling conventions. Alternatively, delegates can be thought of as a one method interface, without the interface.

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.

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"));
}

.NET include a set of pre-defined delegate types - Func<> and Action<> - that be used in many situations, without the requirement to define new types. The example above can be re-written to no longer defined the reverse delegate and instead define the rev variable as a Func<string,string>. The program will function the same.

Func<string,string> rev = ReverseString;

Lambdas are 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 as a Linq select clause.

You can see the use of lambda as a linq select clause in the Linq section above. The following example rewrites the program above using the more compact lambda syntax. Note that an explictly defined delegate could still be used, instead of Func<>.

static void Main(string[] args)
{
Func<string,string> rev = (s) => {return new string(s.Reverse().ToArray());};

Console.WriteLine(rev("a string"));
}

The following example demonstrated the use of a lambda as an event handler.

public MainWindow()
{
InitializeComponent();

Loaded += (o, e) =>
{
this.Title = "Loaded";
};
}

Native Interop
--------------

.NET provides low-level access to native APIs via the platform invoke or P/Invoke facility. It enables a mapping of .NET types to native types, which the .NET runtime marshalls before calling the native API.

TODO: Examples.

Higher-level native interop can be established with P/Invoke. The COM and WinRT interop systems in the CLR are both built on top of P/Invoke. The Java and Objective-C interop systems provided by Xamarin on top of Mono are fundamentally the same.

Unsafe Code
-----------

The CLR enables the ability to acccess native memory and do pointer arithmetic. These operations are needed for some algortithms and for calling some native APIs. The use of these capabilities is discouraged, since you no longer get the benefit of verifiability, nor will your code be allowed to run in all environments. The best practice is to confine unsafe code as much as possible and that the vast majority of code is type-safe.

TODO: Examples.

Notes
=====

The term ".NET runtime" is used throughout the document to accomodate for the multiple implementations of .NET, such as CLR, Mono, IL2CPP and others. The more specific names are only used if needed.

This document is not intended to be historical in nature, but describe the .NET platform as it is now. It isn't important whether a .NET feature has always been available or was only recently introduced, only that it is important enough to highlight and discuss.