.NET 教程Tour of .NET

.NET 是一个通用开发平台。.NET is a general purpose development platform. 它具有几项关键功能,例如支持多种编程语言、异步和并发编程模型以及本机互操作性,可以支持跨多个平台的各种方案。It has several key features, such as support for multiple programming languages, asynchronous and concurrent programming models, and native interoperability, which enable a wide range of scenarios across multiple platforms.

本文还提供了 .NET 的一些关键功能的指导教程。This article offers a guided tour through some of the key features of the .NET. 请参阅 .NET 体系结构组件主题,了解 .NET 的各体系结构部分及其用途。See the .NET Architectural Components topic to learn about the architectural pieces of .NET and what they're used for.

如何运行代码示例How to run the code samples

要了解如何设置开发环境以运行代码示例,请参阅入门主题。To learn how to set up a development environment to run the code samples, see the Getting Started topic. 将代码示例从此页复制并粘贴到你的环境中以执行它们。Copy and paste code samples from this page into your environment to execute them.

编程语言Programming languages

.NET 支持多种编程语言。.NET supports multiple programming languages. .NET 实现可实现公共语言基础结构 (CLI),除其他事项外,它指定与语言无关的运行时和语言互操作性。The .NET implementations implement the Common Language Infrastructure (CLI), which among other things specifies a language-independent runtime and language interoperability. 这意味着可选择任意 .NET 语言在 .NET 上生成应用和服务。This means that you choose any .NET language to build apps and services on .NET.

Microsoft 积极开发和支持三种 .NET 语言:C#、F# 和 Visual Basic (VB)。Microsoft actively develops and supports three .NET languages: C#, F#, and Visual Basic (VB).

  • C# 是一种简单、强大、类型安全、面向对象的语言,同时保留了 C 语言的表达力度和简洁性。C# is simple, powerful, type-safe, and object-oriented, while retaining the expressiveness and elegance of C-style languages. 熟悉 C 和类似语言的任何人在适应 C# 的过程中几乎不会遇到什么问题。Anyone familiar with C and similar languages finds few problems in adapting to C#. 请查看 C# 指南,了解有关 C# 的详细信息。Check out the C# Guide to learn more about C#.

  • F# 是一种跨平台、功能优先的编程语言,它也支持传统的面向对象的编程和命令式编程。F# is a cross-platform, functional-first programming language that also supports traditional object-oriented and imperative programming. 请查看 F# 指南,了解有关 F# 的详细信息。Check out the F# Guide to learn more about F#.

  • Visual Basic 是一种简单易学的语言,用于生成在 .NET 上运行的各种应用。Visual Basic is an easy language to learn that you use to build a variety of apps that run on .NET. 在 .NET 语言中,VB 的语法最接近于人类的普通用语,通常使软件开发新手更容易上手。Among the .NET languages, the syntax of VB is the closest to ordinary human language, often making it easier for people new to software development.

自动内存管理Automatic memory management

.NET 使用垃圾回收 (GC) 为程序提供自动内存管理。.NET uses garbage collection (GC) to provide automatic memory management for programs. GC 以一种“懒散”的方式进行内存管理,它优先考虑应用吞吐量,而不是立即回收内存。The GC operates on a lazy approach to memory management, preferring app throughput to the immediate collection of memory. 要了解有关 .NET GC 的详细信息,请查看垃圾回收 (GC) 的基础To learn more about the .NET GC, check out Fundamentals of garbage collection (GC).

以下两行代码都会分配内存:The following two lines both allocate memory:

var title = ".NET Primer";
var list = new List<string>();

无法使用任何类似的关键字来取消分配内存,因为当垃圾回收器通过其计划的运行规则回收内存时,会自动取消分配。There's no analogous keyword to de-allocate memory, as de-allocation happens automatically when the garbage collector reclaims the memory through its scheduled run.

垃圾回收站是一种帮助确保内存安全的服务。The garbage collector is one of the services that help ensure memory safety. 如果某个程序仅访问分配的内存,则该程序就是内存安全的。A program is memory safe if it accesses only allocated memory. 例如,运行时可确保应用不会访问超过数组边界的未分配内存。For instance, the runtime ensures that an app doesn't access unallocated memory beyond the bounds of an array.

下例中,运行时引发 InvalidIndexException 异常,强制实现内存安全:In the following example, the runtime throws an InvalidIndexException exception to enforce memory safety:

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

处理未托管的资源Working with unmanaged resources

部分对象会引用未托管的资源Some objects reference unmanaged resources. 未托管的资源是指不由 .NET 运行时自动维护的资源。Unmanaged resources are resources that aren't automatically maintained by the .NET runtime. 例如,文件句柄就是未托管的资源。For example, a file handle is an unmanaged resource. FileStream 对象是一个托管对象,但它引用未托管的文件句柄。A FileStream object is a managed object, but it references a file handle, which is unmanaged. 用完 FileStream 之后,需要释放文件句柄。When you're done using the FileStream, you need to release the file handle.

在 .NET 中,引用未托管资源的对象会实现 IDisposable 接口。In .NET, objects that reference unmanaged resources implement the IDisposable interface. 用完对象后,需调用此对象的 Dispose() 方法,该方法会释放所有托管资源。When you're done using the object, you call the object's Dispose() method, which is responsible for releasing any unmanaged resources. .NET 语言为此类对象提供了一种便捷的 using 语法,如下例所示:.NET languages provide a convenient using syntax for such objects, as shown in the following example:

using System.IO;

using (FileStream stream = GetFileStream(context))
{
    // Operations on the stream
}

using 块完成后,.NET 运行时会自动调用 stream 对象的 Dispose() 方法,该方法会释放文件句柄。Once the using block completes, the .NET runtime automatically calls the stream object's Dispose() method, which releases the file handle. 如果某异常造成控件退出块,则运行时也会执行此操作。The runtime also does this if an exception causes control to leave the block.

有关更多详细信息,请参阅下列主题:For more details, see the following topics:

类型安全Type safety

对象是特定类型的实例。An object is an instance of a specific type. 给定对象允许的唯一操作属于特定的类型。The only operations allowed for a given object are those of its type. Dog 类型可能具有 JumpWagTail 方法,但没有 SumTotal 方法。A Dog type may have Jump and WagTail methods but not a SumTotal method. 程序只调用属于给定类型的方法。A program only calls the methods belonging to a given type. 所有其他调用会导致编译时错误或运行时异常(如果使用动态功能或 object)。All other calls result in either a compile-time error or a run-time exception (in case of using dynamic features or object).

.NET 语言面向对象,包含基类和派生类的层次结构。.NET languages are object-oriented with hierarchies of base and derived classes. .NET 运行时仅允许与对象层次结构相符的对象强制转换和调用。The .NET runtime only allows object casts and calls that align with the object hierarchy. 请记住,任何 .NET 语言中定义的每个类型都派生自 Object 基类型。Remember that every type defined in any .NET language derives from the base Object type.

Dog dog = AnimalShelter.AdoptDog(); // Returns a Dog type.
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.

使用类型安全还有助于强制实施封装,因为它可以保证访问器关键字的保真度。Type safety is also used to help enforce encapsulation by guaranteeing the fidelity of the accessor keywords. 访问器关键字是控制其他代码访问给定类型的成员的项目。Accessor keywords are artifacts which control access to members of a given type by other code. 这些关键字通常用于某个类型中用来管理类型行为的各种数据。These are usually used for various kinds of data within a type that are used to manage its behavior.

private Dog _nextDogToBeAdopted = AnimalShelter.AdoptDog()

C#、VB 和 F# 支持本地类型推理。C#, VB, and F# support local type inference. 类型推理是指编译器根据右侧的表达式推断左侧表达式的类型。Type inference means that the compiler deduces the type of the expression on the left-hand side from the expression on the right-hand side. 这并不意味着类型安全遭到破坏或规避。This doesn't mean that the type safety is broken or avoided. 生成的类型具有一个隐含所有信息的强类型。The resulting type does have a strong type with everything that implies. 在上例中,重写了 dog 以介绍类型推理,示例的其余部分保持不变:From the previous example, dog is rewritten to introduce type inference, and the remainder of the example is unchanged:

var dog = AnimalShelter.AdoptDog();
var pet = (Pet)dog;
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

与在 C# 和 VB 中找到的方法本地类型推理相比,F# 的类型推理能力更强。F# has even further type inference capabilities than the method-local type inference found in C# and VB. 若要了解详细信息,请参阅类型推理To learn more, see Type Inference.

委托和 lambdaDelegates and lambdas

委托用方法签名表示。A delegate is represented by a method signature. 可将任何使用该签名的方法分配给该委托,调用该委托时会执行这些方法。Any method with that signature can be assigned to the delegate and is executed when the delegate is invoked.

委托类似于 C++ 函数指针,只是它们是类型安全的。Delegates are like C++ function pointers except that they're type safe. 它们是 CLR 类型系统中某种断开关联的方法。They're a kind of disconnected method within the CLR type system. 正则方法会关联到某个类,只能通过静态或实例调用约定来直接调用。Regular methods are attached to a class and are only directly callable through static or instance calling conventions.

在 .NET 中,委托通常用于事件处理程序、定义异步操作以及 lambda 表达式,它们是 LINQ 的基础。In .NET, delegates are commonly used in event handlers, in defining asynchronous operations, and in lambda expressions, which are a cornerstone of LINQ. 有关详细信息,请参见委托和 lambda 主题。Learn more in the Delegates and lambdas topic.

泛型Generics

泛型可让程序员在设计类时引入一个类型参数,这样,客户端代码(类型的使用者)便可指定要使用哪个确切的类型来取代类型参数。Generics allow the programmer to introduce a type parameter when designing their classes that allows the client code (the users of the type) to specify the exact type to use in place of the type parameter.

添加泛型的目的是帮助程序员实现通用数据结构。Generics were added to help programmers implement generic data structures. 在泛型问世之前,要将 List 等类型用作泛型,必须处理 object 类型的元素。Before their arrival in order for a type such as the List type to be generic, it would have to work with elements that were of type object. 这会造成各种性能和语义问题,甚至造成微妙的运行时错误。This had various performance and semantic problems, along with possible subtle runtime errors. 例如,当数据结构包含整数和字符串时,如果在处理列表的成员时引发 InvalidCastException,则就会出现运行时错误这种非常棘手的问题。The most notorious of the latter is when a data structure contains, for instance, both integers and strings, and an InvalidCastException is thrown on working with the list's members.

以下示例显示了使用 List<T> 类型实例运行的基本程序:The following sample shows a basic program running using an instance of List<T> types:

using System;
using System.Collections.Generic;

namespace GenericsSampleShort
{
    public static void Main(string[] args)
    {
        // List<string> is the client way of specifying the actual type for the type parameter T
        List<string> listOfStrings = new List<string> { "First", "Second", "Third" };

        // listOfStrings can accept only strings, both on read and write.
        listOfStrings.Add("Fourth");

        // Below will throw a compile-time error, since the type parameter
        // specifies this list as containing only strings.
        listOfStrings.Add(1);
    }
}

有关详细信息,请参阅泛型类型(泛型)概述主题。For more information, see the Generic types (Generics) overview topic.

异步编程Async programming

异步编程是 .NET 中的一个先进概念,它在运行时、框架库和 .NET 语言构造中提供异步支持。Async programming is a first-class concept within .NET with async support in the runtime, framework libraries, and .NET language constructs. 在内部,异步编程基于利用操作系统尽可能高效地执行 I/O 绑定型作业的对象(例如 Task)。Internally, they're based on objects (such as Task), which take advantage of the operating system to perform I/O-bound jobs as efficiently as possible.

若要了解有关 .NET 中异步编程的详细信息,请先阅读异步概述主题。To learn more about async programming in .NET, start with the Async overview topic.

语言集成查询 (LINQ)Language Integrated Query (LINQ)

LINQ 是适用于 C# 和 VB 的强大功能集,可用于编写简单的声明性代码来处理数据。LINQ is a powerful set of features for C# and VB that allow you to write simple, declarative code for operating on data. 数据可采用多种形式(例如,内存中对象、SQL 数据库或 XML 文档),但针对每个数据源编写的 LINQ 代码往往没有差别。The data can be in many forms (such as in-memory objects, a SQL database, or an XML document), but the LINQ code you write typically doesn't differ by data source.

若要了解详细信息并查看某部分示例,请参阅 LINQ(语言集成查询)主题。To learn more and see some samples, see the LINQ (Language Integrated Query) topic.

本机互操作性Native interoperability

每个操作系统都有一个应用程序编程接口 (API),用于提供系统服务。Every operating system includes an application programming interface (API) that provides system services. .NET 提供多种方式来调用这些 API。.NET provides several ways to call those APIs.

实现本机互操作性的主要方式是通过“平台调用”(简称 P/Invoke),Linux 和 Windows 平台均支持这种方式。The main way to do native interoperability is via "platform invoke" or P/Invoke for short, which is supported across Linux and Windows platforms. 实现本机互操作性的另一种方式称为“COM 互操作”(仅限 Windows),用于在托管代码中操作 COM 组件A Windows-only way of doing native interoperability is known as "COM interop," which is used to work with COM components in managed code. 这种方式建立在 P/Invoke 基础结构之上,但工作原理略有不同。It's built on top of the P/Invoke infrastructure, but it works in subtly different ways.

针对 Java 和 Objective-C 的 Mono(以及 Xamarin)互操作性支持基本上以类似的方式构建,也就是说,它们运用相同的原理。Most of Mono's (and thus Xamarin's) interoperability support for Java and Objective-C are built similarly, that is, they use the same principles.

有关本机互操作性的详细信息,请参阅本机互操作性一文。For more information about native interoperability, see the Native interoperability article.

不安全代码Unsafe code

根据语言支持,CLR 可通过 unsafe 代码访问本机内存和执行指针算术运算。Depending on language support, the CLR lets you access native memory and do pointer arithmetic via unsafe code. 某些算法和系统互操作性需要这些操作。These operations are needed for certain algorithms and system interoperability. 尽管不安全代码的功能强大,但除非有必要与系统 API 互操作或实现最高效的算法,否则不建议使用。Although powerful, use of unsafe code is discouraged unless it's necessary to interop with system APIs or implement the most efficient algorithm. 在不同的环境中,不安全代码的执行方式可能不同,使用它还会丧失垃圾回收器和类型安全带来的好处。Unsafe code may not execute the same way in different environments and also loses the benefits of a garbage collector and type safety. 建议尽可能地限制和集中化使用不安全代码,并全面测试该代码。It's recommended to confine and centralize unsafe code as much as possible and test that code thoroughly.

以下示例是 StringBuilder 类中 ToString() 方法的修改版本。The following example is a modified version of the ToString() method from the StringBuilder class. 它演示了如何使用 unsafe 代码直接移动内存区块,从而有效实现某种算法:It illustrates how using unsafe code can efficiently implement an algorithm by moving around chunks of memory directly:

public override String ToString()
{
    if (Length == 0)
        return String.Empty;

    string ret = string.FastAllocateString(Length);
    StringBuilder chunk = this;
    unsafe
    {
        fixed (char* destinationPtr = ret)
        {
            do
            {
                if (chunk.m_ChunkLength > 0)
                {
                    // Copy these into local variables so that they are stable even in the presence of ----s (hackers might do this)
                    char[] sourceArray = chunk.m_ChunkChars;
                    int chunkOffset = chunk.m_ChunkOffset;
                    int chunkLength = chunk.m_ChunkLength;

                    // Check that we will not overrun our boundaries.
                    if ((uint)(chunkLength + chunkOffset) <= ret.Length && (uint)chunkLength <= (uint)sourceArray.Length)
                    {
                        fixed (char* sourcePtr = sourceArray)
                            string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength);
                    }
                    else
                    {
                        throw new ArgumentOutOfRangeException("chunkLength", Environment.GetResourceString("ArgumentOutOfRange_Index"));
                    }
                }
                chunk = chunk.m_ChunkPrevious;
            } while (chunk != null);
        }
    }
    return ret;
}

后续步骤Next steps

如果对 C# 功能的教程感兴趣,请参阅 C# 教程If you're interested in a tour of C# features, check out Tour of C#.

如果对 F# 功能的教程感兴趣,请参阅 F# 教程If you're interested in a tour of F# features, see Tour of F#.

如果想开始自己编写代码,请访问入门If you want to get started with writing code of your own, visit Getting Started.

要了解 .NET 的重要组件,请参阅 .NET 体系结构组件To learn about important components of .NET, check out .NET Architectural Components.