Chapter 5. Framework Overview

Almost all of the capabilities of .NET Core are exposed via a vast set of managed types. These types are organized into hierarchical namespaces and packaged into a set of assemblies, which together with the CLR comprise the .NET Core platform.

Some of the .NET types are used directly by the CLR and are essential for the managed hosting environment. These types reside in an assembly called System.Private.CoreLib.dll. They include C#’s built-in types as well as the basic collection classes, and types for stream processing, serialization, reflection, threading, and native interoperability.

Note

System.Private.CoreLib.dll replaces .NET Framework’s mscorlib.dll. Many places in the official documentation still refer to mscorlib.

At a level above this are additional types that “flesh out” the CLR-level functionality, providing features such as XML, JSON, networking, and Language-Integrated Query (LINQ). These comprise the Base Class Library (BCL). Sitting above this are application frameworks, which provide APIs for developing particular kinds of applications such as web or rich client.

In this chapter, we provide the following:

  • An overview of the BCL (which we cover in the rest of the book)

  • A high-level summary of the available application frameworks

.NET Standard

In Chapter 1, we said that there are four major framework choices:

  • .NET Core

  • UWP

  • Mono + Xamarin (for mobile device development)

  • .NET Framework

Each framework contains its own CLR and BCL. The good news is that at the time of .NET Core 2.0’s release, these frameworks converged in their core functionality, and now all offer a BCL with similar types and members. This commonality has been formalized into a standard called .NET Standard 2.0.

.NET Standard 2.0

A library that targets .NET Standard 2.0 instead of a specific framework is usefully portable. The same assembly will run without modification on most of today’s popular frameworks, including the following:

  • .NET Core 2.0+

  • UWP 10.0.16299+

  • Mono 5.4+

  • .NET Framework 4.6.1+

To target .NET Standard 2.0, add the following to your .csproj file:

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  <PropertyGroup>
Note

.NET Standard is not a framework; it’s merely a specification describing a minimum baseline of functionality (types and members) that guarantees compatibility with a certain set of frameworks. The concept is similar to C# interfaces: .NET Standard is like an interface that concrete types (frameworks) can implement.

.NET Standard 2.1

.NET Core 3 also supports .NET Standard 2.1, a superset of .NET Standard 2.0 that exposes most of the additional types that were introduced with .NET Core 3. However, .NET Standard 2.1 is not supported by any version of .NET Framework (and not even by UWP as of this writing), making it much less useful than .NET Standard 2.0.

The following APIs, in particular, are available in .NET Standard 2.1 (but not .NET Standard 2.0):

Older .NET Standards

There are also older .NET Standards, most notably 1.1, 1.2, 1.3, and 1.6. A higher-numbered standard is always a strict superset of a lower-numbered standard. For instance, if you write a library that targets .NET Standard 1.6, you will support not only recent versions of the major frameworks, but also .NET Core 1.0. And if you target .NET Standard 1.3, you support everything we’ve already mentioned plus .NET Framework 4.6.0. The following table elaborates:

If you target... You also support...
Standard 1.6 .NET Core 1.0
Standard 1.3 Above plus .NET 4.6.0
Standard 1.2 Above plus .NET 4.5.1, Windows Phone 8.1, WinRT for Windows 8.1
Standard 1.1 Above plus .NET 4.5.0, Windows Phone 8.0, WinRT for Windows 8.0
Note

The 1.x standards lack thousands of APIs that are present in 2.0, including much of what we describe in this book. This can make targeting a 1.x standard significantly more challenging, especially if you need to integrate existing code or libraries.

You can also think of .NET Standard as a lowest common denominator. In the case of .NET Standard 2.0, the frameworks that implement it have a similar BCL, so the lowest common denominator is big and useful. However, if you also want compatibility with .NET Core 1.0 (with its significantly cut-down BCL), the lowest common denominator—.NET Standard 1.x—becomes much smaller and less useful.

.NET Framework and .NET Core Compatibility

Because .NET Framework has existed for so long, it’s not uncommon to encounter libraries that are available only for .NET Framework (with no .NET Standard or .NET Core equivalent). To help mitigate this situation, .NET Core projects are permitted to reference .NET Framework assemblies, with the following provisos:

  • An exception is thrown should the .NET Framework assembly call an API that’s not supported in .NET Core.

  • Nontrivial dependencies might fail to resolve.

In practice, it’s most likely to work with assemblies that perform a simple function, such as wrapping an unmanaged DLL, or that rely on a single well-supported API, such as WPF or Windows Forms.

Framework and C# Language Versions

The C# compiler chooses a language version automatically based on the framework that your project targets:

  • For .NET Core 3.x and .NET Standard 2.1, it chooses C# 8.

  • For .NET Core 2.x, .NET Framework, and .NET Standard 2.0 and below, it chooses C# 7.3.

This is because C# 8’s new features rely on types that are available only in .NET Core 3+ or .NET Standard 2.1+.

Reference Assemblies

When you target .NET Standard, your project implicitly references an assembly called netstandard.dll, which contains all of the allowable types and members for your chosen version of .NET Standard. This is called a reference assembly because it exists only for the benefit of the compiler and contains no compiled code. At runtime, the “real” assemblies are identified through assembly redirection attributes (the choice of assemblies will depend on which framework and platform the assembly eventually runs on).

Interestingly, a similar thing happens when you target .NET Core. Your project implicitly references a set of reference assemblies whose types mirror what’s in the runtime assemblies for the chosen .NET Core version. This helps with versioning and cross-platform compatibility, and also allows you to target a different .NET Core version than what is installed on your machine. For instance, if you’ve installed .NET Core 3, your project can still target .NET Core 2.2, and thanks to a set of reference assemblies, the compiler will see only the types and members available to .NET Core 2.2.

The CLR and BCL

System Types

The most fundamental types live directly in the System namespace. These include C#’s built-in types, the Exception base class, the Enum, Array, and Delegate base classes, and Nullable, Type, DateTime, TimeSpan, and Guid. The System namespace also includes types for performing mathematical functions (Math), generating random numbers (Random), and converting between various types (Convert and BitConverter).

Chapter 6 describes these types as well as the interfaces that define standard protocols used across the Framework for such tasks as formatting (IFormattable) and order comparison (IComparable).

The System namespace also defines the IDisposable interface and the GC class for interacting with the garbage collector, which we cover in Chapter 12.

Text Processing

The System.Text namespace contains the StringBuilder class (the editable or mutable cousin of string) and the types for working with text encodings, such as UTF-8 (Encoding and its subtypes). We cover this in Chapter 6.

The System.Text.RegularExpressions namespace contains types that perform advanced pattern-based search-and-replace operations; we describe these in Chapter 26.

Collections

.NET Core offers a variety of classes for managing collections of items. These include both list- and dictionary-based structures; they work in conjunction with a set of standard interfaces that unify their common characteristics. All collection types are defined in the following namespaces, covered in Chapter 7:

System.Collections                 // Nongeneric collections
System.Collections.Generic         // Generic collections
System.Collections.Specialized     // Strongly typed collections
System.Collections.ObjectModel     // Bases for your own collections
System.Collections.Concurrent      // Thread-safe collection (Chapter 23)

Querying

LINQ allows you to perform type-safe queries over local and remote collections (e.g., SQL Server tables) and is described in Chapters 8 through 10. A big advantage of LINQ is that it presents a consistent querying API across a variety of domains. The essential types reside in the following namespaces:

System.Linq                  // LINQ to Objects and PLINQ
System.Linq.Expressions      // For building expressions manually
System.Xml.Linq              // LINQ to XML

XML and JSON

XML and JSON are widely supported in .NET Core. Chapter 10 focuses entirely on LINQ to XML—a lightweight XML Document Object Model (DOM) that can be constructed and queried through LINQ. Chapter 11 covers the performant low-level XML reader/writer classes, XML schemas and stylesheets, and the types for working with JSON:

System.Xml                // XmlReader, XmlWriter
System.Xml.Linq           // The LINQ to XML DOM
System.Xml.Schema         // Support for XSD
System.Xml.Serialization  // Declarative XML serialization for .NET types
System.Xml.XPath          // XPath query language
System.Xml.Xsl            // Stylesheet support

System.Text.Json          // JSON reader/writer and document object model

In Chapter 17 (Serialization), we cover the JSON serializer.

Diagnostics

In Chapter 13, we cover logging and assertion and describe how to interact with other processes, write to the Windows event log, and handle performance monitoring. The types for this are defined in and under System.Diagnostics.

Concurrency and Asynchrony

Many modern applications need to deal with more than one thing happening at a time. Since C# 5.0, this has become easier through asynchronous functions and high-level constructs such as tasks and task combinators. Chapter 14 explains all of this in detail, after starting with the basics of multithreading. Types for working with threads and asynchronous operations are in the System.Threading and System.Threading.Tasks namespaces.

Streams and I/O

.NET Core provides a stream-based model for low-level I/O. Streams are typically used to read and write directly to files and network connections, and can be chained or wrapped in decorator streams to add compression or encryption functionality. Chapter 15 describes the stream architecture as well as the specific support for working with files and directories, compression, pipes, and memory-mapped files. The Stream and I/O types are defined in and under the System.IO namespace, and the Windows Runtime (WinRT) types for file I/O are in and under Windows​.Storage.

Networking

You can directly access standard network protocols such as HTTP, FTP, TCP/IP, and SMTP via the types in System.Net. In Chapter 16, we demonstrate how to communicate using each of these protocols, starting with simple tasks such as downloading from a web page and finishing with using TCP/IP directly to retrieve POP3 email. Here are the namespaces we cover:

System.Net
System.Net.Http          // HttpClient
System.Net.Mail          // For sending mail via SMTP
System.Net.Sockets       // TCP, UDP, and IP

Serialization

The Framework provides several systems for saving and restoring objects to a binary or text representation. Such systems can be required for communication as well as saving and restoring objects to a file. In Chapter 17, we cover the three major serialization engines: the binary serializer, the JSON serializer, and the XML serializer. The types for serialization reside in the following namespaces:

System.Runtime.Serialization
System.Xml.Serialization
System.Text.Json

Assemblies, Reflection, and Attributes

The assemblies into which C# programs compile comprise executable instructions (stored as IL) and metadata, which describes the program’s types, members, and attributes. Through reflection, you can inspect this metadata at runtime and do such things as dynamically invoke methods. With Reflection.Emit, you can construct new code on the fly.

In Chapter 18, we describe the makeup of assemblies and how to dynamically load and isolate them. In Chapter 19, we cover reflection and attributes—describing how to inspect metadata, dynamically invoke functions, write custom attributes, emit new types, and parse raw IL. The types for using reflection and working with assemblies reside in the following namespaces:

System
System.Reflection
System.Reflection.Emit

Dynamic Programming

In Chapter 20, we look at some of the patterns for dynamic programming and utilizing the Dynamic Language Runtime (DLR). We describe how to implement the Visitor pattern, write custom dynamic objects, and interoperate with IronPython. The types for dynamic programming are in System.Dynamic.

Cryptography

.NET Core provides extensive support for popular hashing and encryption protocols. In Chapter 21, we cover hashing, symmetric and public-key encryption, and the Windows Data Protection API. The types for this are defined in:

System.Security
System.Security.Cryptography

Advanced Threading

C#’s asynchronous functions make concurrent programming significantly easier because they lessen the need for lower-level techniques. However, there are still times when you need signaling constructs, thread-local storage, reader/writer locks, and so on. Chapter 22 explains this in depth. Threading types are in the System.Threading namespace.

Parallel Programming

In Chapter 23, we cover in detail the libraries and types for leveraging multicore processors, including APIs for task parallelism, imperative data parallelism, and functional parallelism (PLINQ).

Span<T> and Memory<T>

To help with micro-optimizing performance hotspots, the CLR provides a number of types to help you program in such a way as to reduce the load on the memory manager. Two of the key types are Span<T> and Memory<T>, which we describe in Chapter 24.

Native and COM Interoperability

You can interoperate with both native and Component Object Model (COM) code. Native interoperability allows you to call functions in unmanaged DLLs, register callbacks, map data structures, and interoperate with native data types. COM interoperability allows you to call COM types (on Windows machines), and expose .NET Core types to COM. The types that support these functions are in System.Runtime.InteropServices, and we cover them in Chapter 25.

Regular Expressions

In Chapter 26, we cover how you can use regular expressions to match character patterns in strings.

The Roslyn Compiler

The C# compiler itself is written in C#—the project is called “Roslyn,” and the libraries are available as NuGet packages. With these libraries, you can utilize the compiler’s functionality in many ways besides compiling source code to an assembly, such as writing code analysis and refactoring tools. We cover this topic in Chapter 27.

Application Frameworks

UI–based applications can be divided into two categories: thin client, which amounts to a website, and rich client, which is a program the end user must download and install on a computer or mobile device.

For writing thin-client applications in C#, there’s ASP.NET Core, which runs on Windows, Linux, and macOS. ASP.NET Core is also designed for writing web APIs.

For rich-client applications, there are a choice of APIs:

  • The Windows Desktop framework includes the WPF and Windows Forms APIs, and runs on Windows 7/8/10 desktop

  • UWP runs on Windows 10 desktop and devices

  • Xamarin runs on iOS and Android mobile devices

There are also third-party libraries, such as Avalonia, which offers cross-platform UI support.

ASP.NET Core

ASP.NET Core is a lightweight modular successor to ASP.NET, with support for the popular Model-View-Controller (MVC) pattern. ASP.NET Core is suitable for creating websites, REST-based web APIs, and microservices. It can also run in conjunction with two popular single-page-application frameworks: React and Angular.

ASP.NET Core runs on Windows, Linux, and macOS and can self-host in a custom process. Unlike its predecessor (ASP.NET), ASP.NET Core is not dependent on System.Web and the historical baggage of web forms.

As with any thin-client architecture, ASP.NET Core offers the following general advantages over rich clients:

  • There is zero deployment at the client end.

  • The client can run on any platform that supports a web browser.

  • Updates are easily deployed.

Windows Desktop

The Windows Desktop application framework offers a choice of two UI APIs for writing rich-client applications: WPF and Windows Forms. Both APIs run on Windows Desktop/Server 7 through 10.

WPF

WPF was introduced in 2006, and has been enhanced ever since. Unlike its predecessor, Windows Forms, WPF explicitly renders controls using DirectX, with the following benefits:

  • It supports sophisticated graphics, such as arbitrary transformations, 3D rendering, multimedia, and true transparency. Skinning is supported through styles and templates.

  • Its primary measurement unit is not pixel-based, so applications display correctly at any DPI setting.

  • It has extensive and flexible layout support, which means that you can localize an application without danger of elements overlapping.

  • Its use of DirectX makes rendering fast and able to take advantage of graphics hardware acceleration.

  • It offers reliable data binding.

  • UIs can be described declaratively in XAML files that can be maintained independent of the “code-behind” files—this helps to separate appearance from functionality.

WPF takes some time to learn due to its size and complexity. The types for writing WPF applications are in the System.Windows namespace and all subnamespaces except for System.Windows.Forms.

Windows Forms

Windows Forms is a rich-client API that shipped with the first version of .NET Framework in 2000. Compared to WPF, Windows Forms is a relatively simple technology that provides most of the features you need in writing a typical Windows application. It also has significant relevancy in maintaining legacy applications. But compared to WPF, it has numerous drawbacks, most of which stem from it being a wrapper over GDI+ and the Win32 control library:

  • Although it provides mechanisms for DPI awareness, it’s still too easy to write applications that break on clients whose DPI settings differ from the developer’s.

  • The API for drawing nonstandard controls is GDI+, which, although reasonably flexible, is slow in rendering large areas (and, without double buffering, might flicker).

  • Controls lack true transparency.

  • Most controls are noncompositional. For instance, you can’t put an image control inside a tab control header. Customizing list views, combo boxes, and tab controls in a way that would be trivial with WPF is time consuming and painful in Windows Forms.

  • Dynamic layout is difficult to correctly implement.

The last point is an excellent reason to favor WPF over Windows Forms—even if you’re writing a business application that needs just a UI and not a “user experience.” The layout elements in WPF, such as Grid, make it easy to assemble labels and text boxes such that they always align—even after language-changing localization—without messy logic and without any flickering. Further, you don’t need to bow to the lowest common denominator in screen resolution—WPF layout elements have been designed from the outset to adapt properly to resizing.

On the positive side, Windows Forms is relatively simple to learn and still has a good number of third-party controls.

The Windows Forms types are in the System.Windows.Forms (in System.Windows.Forms.dll) and System.Drawing (in System.Drawing.dll) namespaces. The latter also contains the GDI+ types for drawing custom controls.

UWP

UWP is a rich-client API for writing touch-first UIs that target Windows 10 desktop and devices. The word “Universal” refers to its ability to run on a range of Windows 10 devices, including Xbox, Surface Hub, and Hololens. However, it’s not compatible with earlier versions of Windows, including Windows 7 and Windows 8/8.1.

The UWP API uses XAML and is somewhat similar to WPF. Here are its key differences:

  • The primary mode of distribution for UWP apps is the Windows Store.

  • UWP apps run in a sandbox to lessen the threat of malware, which means that they cannot perform tasks such as reading or writing arbitrary files, and they cannot run with administrative elevation.

  • UWP relies on WinRT types that are part of the operating system (Windows), not the managed framework. This means that when writing apps, you must nominate a Windows 10 version range (such as Windows 10 build 17763 to Windows 10 build 18362). This means that you either need to target an old API, or require that your customers install the latest Windows update.

To address the last point, Microsoft is introducing WinUI 3, which transfers the WinRT APIs from the operating system to the framework. WinUI 3 will also help to bridge the divide between Windows Desktop and UWP: rather than having to choose one or the other, you’ll be able to mix and match components from each.

The namespaces in UWP are Windows.UI and Windows.UI.Xaml.

Xamarin

Xamarin lets you write mobile apps in C# that target iOS and Android. Xamarin doesn’t run on .NET Core, but on Mono (a derivation of the open source Mono framework). See Xamarin’s website for more information.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset