ВступлениеIntroduction

C# (произносится как "си шарп") — простой, современный объектно-ориентированный и типобезопасный язык программирования.C# (pronounced "See Sharp") is a simple, modern, object-oriented, and type-safe programming language. C#имеет корни в семействе языков C и будет сразу же знакомы программистам C, C++и Java.C# has its roots in the C family of languages and will be immediately familiar to C, C++, and Java programmers. C#стандартизовано в стандарте ECMA -334 , а также стандарт ISO/IEC в качестве стандарта iso/IEC 23270 .C# is standardized by ECMA International as the ECMA-334 standard and by ISO/IEC as the ISO/IEC 23270 standard. C# Компилятор майкрософт для .NET Framework — это согласованная реализация обоих стандартов.Microsoft's C# compiler for the .NET Framework is a conforming implementation of both of these standards.

C# является объектно-ориентированным языком, но поддерживает также и компонентно-ориентированное программирование.C# is an object-oriented language, but C# further includes support for component-oriented programming. Разработка современных приложений все больше тяготеет к созданию программных компонентов в форме автономных и самоописательных пакетов, реализующих отдельные функциональные возможности.Contemporary software design increasingly relies on software components in the form of self-contained and self-describing packages of functionality. Важная особенность таких компонентов — это модель программирования на основе свойств, методов и событий. Каждый компонент имеет атрибуты, предоставляющие декларативные сведения о компоненте, а также встроенные элементы документации.Key to such components is that they present a programming model with properties, methods, and events; they have attributes that provide declarative information about the component; and they incorporate their own documentation. C#предоставляет языковые конструкции для непосредственной поддержки этих концепций, делая C# очень естественный язык для создания и использования программных компонентов.C# provides language constructs to directly support these concepts, making C# a very natural language in which to create and use software components.

Вот лишь несколько функций языка C#, обеспечивающих надежность и устойчивость приложений: Сборка мусора автоматически освобождает память, занятую неиспользуемыми объектами; обработка исключений предоставляет структурированный и расширяемый подход к обнаружению ошибок и восстановлению. и строго типизированная структура языка делает невозможным чтение из неинициализированных переменных, индексация массивов за пределами их границ или выполнение непроверенных приведений типов.Several C# features aid in the construction of robust and durable applications: Garbage collection automatically reclaims memory occupied by unused objects; exception handling provides a structured and extensible approach to error detection and recovery; and the type-safe design of the language makes it impossible to read from uninitialized variables, to index arrays beyond their bounds, or to perform unchecked type casts.

В C# существует единая система типов.C# has a unified type system. Все типы C#, включая типы-примитивы, такие как int и double, наследуют от одного корневого типа object.All C# types, including primitive types such as int and double, inherit from a single root object type. Таким образом, все типы используют общий набор операций, и значения любого типа можно хранить, передавать и обрабатывать схожим образом.Thus, all types share a set of common operations, and values of any type can be stored, transported, and operated upon in a consistent manner. Кроме того, C# поддерживает пользовательские ссылочные типы и типы значений, позволяя как динамически выделять память для объектов, так и хранить упрощенные структуры в стеке.Furthermore, C# supports both user-defined reference types and value types, allowing dynamic allocation of objects as well as in-line storage of lightweight structures.

Чтобы гарантировать, C# что программы и библиотеки могут развиваться с течением времени в соответствии с их совместимостью, существенное внимание уделяется разработке версий C#.To ensure that C# programs and libraries can evolve over time in a compatible manner, much emphasis has been placed on versioning in C#'s design. Многие языки программирования обходят вниманием этот вопрос, и в результате программы на этих языках ломаются чаще, чем хотелось бы, при выходе новых версий зависимых библиотек.Many programming languages pay little attention to this issue, and, as a result, programs written in those languages break more often than necessary when newer versions of dependent libraries are introduced. C#Аспекты проектирования, на которые непосредственно влияют вопросы управления версиями, включают отдельные virtual модификаторы и override , правила для разрешения перегрузки методов и поддержку явных объявлений членов интерфейса.Aspects of C#'s design that were directly influenced by versioning considerations include the separate virtual and override modifiers, the rules for method overload resolution, and support for explicit interface member declarations.

В оставшейся части этой главы описываются ключевые особенности C# языка.The rest of this chapter describes the essential features of the C# language. Несмотря на то, что в последующих главах описываются правила и исключения в детализированных и иногда математических целях, эта глава стремится к ясности и краткой информации за счет полноты.Although later chapters describe rules and exceptions in a detail-oriented and sometimes mathematical manner, this chapter strives for clarity and brevity at the expense of completeness. Цель заключается в том, чтобы предоставить читателям введение в язык, который упростит написание первых программ и читает последующие главы.The intent is to provide the reader with an introduction to the language that will facilitate the writing of early programs and the reading of later chapters.

Здравствуй, мирHello world

Для первого знакомства с языком программирования традиционно используется программа "Hello, World".The "Hello, World" program is traditionally used to introduce a programming language. Вот ее пример на C#:Here it is in C#:

using System;

class Hello
{
    static void Main() {
        Console.WriteLine("Hello, World");
    }
}

Файлы исходного кода C# обычно имеют расширение .cs.C# source files typically have the file extension .cs. При условии, что программа "Hello, World" хранится в файле hello.cs, программу можно скомпилировать с помощью компилятора Майкрософт C# , используя командную строку.Assuming that the "Hello, World" program is stored in the file hello.cs, the program can be compiled with the Microsoft C# compiler using the command line

csc hello.cs

который создает исполняемую сборку с hello.exeименем.which produces an executable assembly named hello.exe. Выходные данные, создаваемые этим приложением при его запуске, — этоThe output produced by this application when it is run is

Hello, World

Программа "Hello, World" начинается с директивы using, которая ссылается на пространство имен System.The "Hello, World" program starts with a using directive that references the System namespace. Пространства имен позволяют иерархически упорядочивать программы и библиотеки C#.Namespaces provide a hierarchical means of organizing C# programs and libraries. Пространства имен содержат типы и другие пространства имен. Например, пространство имен System содержит несколько типов (в том числе используемый в нашей программе класс Console) и несколько других пространств имен, таких как IO и Collections.Namespaces contain types and other namespaces—for example, the System namespace contains a number of types, such as the Console class referenced in the program, and a number of other namespaces, such as IO and Collections. Директива using, которая ссылается на пространство имен, позволяет использовать типы из этого пространства имен без указания полного имени.A using directive that references a given namespace enables unqualified use of the types that are members of that namespace. Благодаря директиве using в коде программы можно использовать сокращенное имя Console.WriteLine вместо полного варианта System.Console.WriteLine.Because of the using directive, the program can use Console.WriteLine as shorthand for System.Console.WriteLine.

Класс Hello, объявленный в программе "Hello, World", имеет только один член — это метод с именем Main.The Hello class declared by the "Hello, World" program has a single member, the method named Main. Метод объявляется с staticмодификатором. MainThe Main method is declared with the static modifier. Методы экземпляра могут ссылаться на конкретный экземпляр объекта, используя ключевое слово this, а статические методы работают без ссылки на конкретный объект.While instance methods can reference a particular enclosing object instance using the keyword this, static methods operate without reference to a particular object. По стандартному соглашению точкой входа программы является статический метод с именем Main.By convention, a static method named Main serves as the entry point of a program.

Выходные данные программы создаются в методе WriteLine класса Console из пространства имен System.The output of the program is produced by the WriteLine method of the Console class in the System namespace. Этот класс предоставляется .NET Framework библиотеками классов, которые по умолчанию автоматически ссылаются на компилятор Майкрософт C# .This class is provided by the .NET Framework class libraries, which, by default, are automatically referenced by the Microsoft C# compiler. Обратите C# внимание, что у самого себя нет отдельной библиотеки среды выполнения.Note that C# itself does not have a separate runtime library. Вместо этого .NET Framework является библиотекой среды выполнения C#.Instead, the .NET Framework is the runtime library of C#.

Структура программыProgram structure

В C# основными понятиями организационной структуры являются программы, пространства имен, типы, члены и сборки.The key organizational concepts in C# are programs, namespaces, types, members, and assemblies. Программа на языке C# состоит из одного или нескольких файлов.C# programs consist of one or more source files. В программе объявляются типы, которые содержат члены. Эти типы можно организовать в пространства имен.Programs declare types, which contain members and can be organized into namespaces. Примерами типов являются классы и интерфейсы.Classes and interfaces are examples of types. К членам относятся поля, методы, свойства и события.Fields, methods, properties, and events are examples of members. При компиляции программы на C# упаковываются в сборки.When C# programs are compiled, they are physically packaged into assemblies. Сборки .exe обычно имеют расширение файла или .dll, в зависимости от того, реализуют ли они приложения или библиотеки.Assemblies typically have the file extension .exe or .dll, depending on whether they implement applications or libraries.

ПримерThe example

using System;

namespace Acme.Collections
{
    public class Stack
    {
        Entry top;

        public void Push(object data) {
            top = new Entry(top, data);
        }

        public object Pop() {
            if (top == null) throw new InvalidOperationException();
            object result = top.data;
            top = top.next;
            return result;
        }

        class Entry
        {
            public Entry next;
            public object data;
    
            public Entry(Entry next, object data) {
                this.next = next;
                this.data = data;
            }
        }
    }
}

объявляет класс с именем Stack в пространстве имен с именем. Acme.Collectionsdeclares a class named Stack in a namespace called Acme.Collections. Полное имя этого класса: Acme.Collections.Stack.The fully qualified name of this class is Acme.Collections.Stack. Этот класс содержит несколько членов: поле с именем top, два метода с именами Push и Pop, а также вложенный класс с именем Entry.The class contains several members: a field named top, two methods named Push and Pop, and a nested class named Entry. Класс Entry, в свою очередь, содержит три члена: поле с именем next, поле с именем data и конструктор.The Entry class further contains three members: a field named next, a field named data, and a constructor. Если мы сохраним исходный код этого примера в файле acme.cs, то для его компиляции нужно выполнить такую командную строку:Assuming that the source code of the example is stored in the file acme.cs, the command line

csc /t:library acme.cs

Результатом компиляции будет библиотека (код без точки входа Main), сохраненная в сборке с именем acme.dll.compiles the example as a library (code without a Main entry point) and produces an assembly named acme.dll.

Сборки содержат исполняемый код в виде инструкций промежуточного языка (IL), а также символьные данные в форме метаданных.Assemblies contain executable code in the form of Intermediate Language (IL) instructions, and symbolic information in the form of metadata. Перед выполнением сборки ее код на языке IL автоматически преобразуется в код для конкретного процессора. Эту задачу выполняет JIT-компилятор среды .NET CLR (Common Language Runtime).Before it is executed, the IL code in an assembly is automatically converted to processor-specific code by the Just-In-Time (JIT) compiler of .NET Common Language Runtime.

Cборка полностью описывает сама себя и содержит весь код и метаданные, поэтому в C# не используются директивы #include и файлы заголовков.Because an assembly is a self-describing unit of functionality containing both code and metadata, there is no need for #include directives and header files in C#. Чтобы использовать в программе C# открытые типы и члены, содержащиеся в определенной сборке, вам достаточно указать ссылку на эту сборку при компиляции программы.The public types and members contained in a particular assembly are made available in a C# program simply by referencing that assembly when compiling the program. Например, эта программа использует класс Acme.Collections.Stack из сборки acme.dll:For example, this program uses the Acme.Collections.Stack class from the acme.dll assembly:

using System;
using Acme.Collections;

class Test
{
    static void Main() {
        Stack s = new Stack();
        s.Push(1);
        s.Push(10);
        s.Push(100);
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
    }
}

Если программа хранится test.csв файле, то acme.dll при test.cs компиляции /r сборка может быть указана с помощью параметра компилятора:If the program is stored in the file test.cs, when test.cs is compiled, the acme.dll assembly can be referenced using the compiler's /r option:

csc /r:acme.dll test.cs

Эта команда создает исполняемую сборку с именем test.exe, которая при запуске возвращает такие данные:This creates an executable assembly named test.exe, which, when run, produces the output:

100
10
1

В C# исходный текст программы можно хранить в нескольких исходных файлах.C# permits the source text of a program to be stored in several source files. При компиляции многофайловой программы на C# все исходные файлы обрабатываются вместе, и все они могут свободно ссылаться друг на друга. По сути обработка выполняется так, как если бы все исходные файлы были объединены в один большой файл.When a multi-file C# program is compiled, all of the source files are processed together, and the source files can freely reference each other—conceptually, it is as if all the source files were concatenated into one large file before being processed. В C# никогда не используются опережающие объявления, поскольку порядок объявления, за редким исключением, не играет никакой роли.Forward declarations are never needed in C# because, with very few exceptions, declaration order is insignificant. В C# нет требований объявлять только один открытый тип в одном исходном файле, а также имя исходного файла не обязано совпадать с типом, объявляемом в этом файле.C# does not limit a source file to declaring only one public type nor does it require the name of the source file to match a type declared in the source file.

Типы и переменныеTypes and variables

В C# существуют две разновидности типов: ссылочные типы и типы значений.There are two kinds of types in C#: value types and reference types. Переменные типа значений содержат непосредственно данные, а в переменных ссылочных типов хранятся ссылки на нужные данные, которые именуются объектами.Variables of value types directly contain their data whereas variables of reference types store references to their data, the latter being known as objects. Две переменные ссылочного типа могут ссылаться на один и тот же объект, поэтому может случиться так, что операции над одной переменной затронут объект, на который ссылается другая переменная.With reference types, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. Каждая переменная типа значения имеет собственную копию данных, и операции над одной переменной не могут затрагивать другую (за исключением переменных параметров ref и out).With value types, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other (except in the case of ref and out parameter variables).

C#типы значений делятся на простые типы, типы enum, типы структури типы, допускающие значение NULL, C#а ссылочные типы дополнительно делятся на типы классов, типы интерфейсов, массив типыи типы делегатов.C#'s value types are further divided into simple types, enum types, struct types, and nullable types, and C#'s reference types are further divided into class types, interface types, array types, and delegate types.

В следующей таблице представлен обзор C#системы типов.The following table provides an overview of C#'s type system.

КатегорияCategory ОписаниеDescription
Типы значенийValue types Простые типыSimple types Целочисленный со знаком: sbyte, short, int,longSigned integral: sbyte, short, int, long
Целочисленный без знака: byte, ushort, uint,ulongUnsigned integral: byte, ushort, uint, ulong
Символы Юникода: charUnicode characters: char
IEEE-представление с плавающей запятой: float, doubleIEEE floating point: float, double
Десятичный с повышенной точностью: decimalHigh-precision decimal: decimal
Логическое значение: boolBoolean: bool
Типы перечисленияEnum types Пользовательские типы в формате enum E {...}User-defined types of the form enum E {...}
Типы структурStruct types Пользовательские типы в формате struct S {...}User-defined types of the form struct S {...}
Типы, допускающие значения NULLNullable types Расширения других типов значений, допускающие значение nullExtensions of all other value types with a null value
Ссылочные типыReference types Типы классовClass types Исходный базовым классом для всех типов: objectUltimate base class of all other types: object
Строки Юникода: stringUnicode strings: string
Пользовательские типы в формате class C {...}User-defined types of the form class C {...}
Типы интерфейсаInterface types Пользовательские типы в формате interface I {...}User-defined types of the form interface I {...}
Типы массивовArray types Одно- и многомерные, например, int[] и int[,]Single- and multi-dimensional, for example, int[] and int[,]
Тип делегатовDelegate types Определяемые пользователем типы формы, напримерdelegate int D(...)User-defined types of the form e.g. delegate int D(...)

Восемь целочисленных типов обеспечивают поддержку 8-разрядных, 16-разрядных, 32-разрядных и 64-разрядных значений со знаком или без знака.The eight integral types provide support for 8-bit, 16-bit, 32-bit, and 64-bit values in signed or unsigned form.

Два типа с плавающей запятой float , doubleи, представляются с использованием 32-разрядных форматов с двойной точностью с одинарной точностью и 64, имеющих формат IEEE 754.The two floating point types, float and double, are represented using the 32-bit single-precision and 64-bit double-precision IEEE 754 formats.

Тип decimal — это 128-разрядный тип данных для финансовых и денежных расчетов.The decimal type is a 128-bit data type suitable for financial and monetary calculations.

C#используется для представления логических значений — значений true или false. boolC#'s bool type is used to represent boolean values—values that are either true or false.

Обработка знаков и строк в C# выполняется в кодировке Юникода.Character and string processing in C# uses Unicode encoding. Тип char представляет элемент в кодировке UTF-16, а тип string представляет последовательность элементов в кодировке UTF-16.The char type represents a UTF-16 code unit, and the string type represents a sequence of UTF-16 code units.

В следующей таблице представлены C#числовые типы.The following table summarizes C#'s numeric types.

КатегорияCategory ЧислаBits TypeType Диапазон и точностьRange/Precision
Целое со знакомSigned integral 88 sbyte -128... 127-128...127
1616 short -32768... 32, 767-32,768...32,767
3232 int -2147483648... 2, 147, 483, 647-2,147,483,648...2,147,483,647
6464 long -9223372036854775808... 9, 223, 372, 036, 854, 775, 807-9,223,372,036,854,775,808...9,223,372,036,854,775,807
Целочисленный без знакаUnsigned integral 88 byte 0... 2550...255
1616 ushort 0... 65, 5350...65,535
3232 uint 0... 4, 294, 967, 2950...4,294,967,295
6464 ulong 0... 18, 446, 744, 073, 709, 551, 6150...18,446,744,073,709,551,615
С плавающей запятойFloating point 3232 float 1,5 × 10 ^ − 45 – 3,4 × 10 ^ 38, 7-значная точность1.5 × 10^−45 to 3.4 × 10^38, 7-digit precision
6464 double 5,0 × 10 ^ − 324 до 1,7 × 10 ^ 308, с точностью до 15 цифр5.0 × 10^−324 to 1.7 × 10^308, 15-digit precision
DecimalDecimal 128128 decimal 1,0 × 10 ^ − 28 – 7,9 × 10 ^ 28, 28-значная точность1.0 × 10^−28 to 7.9 × 10^28, 28-digit precision

Программы C# используют объявления типов для создания новых типов.C# programs use type declarations to create new types. В объявлении типа указываются имя и члены нового типа.A type declaration specifies the name and the members of the new type. C#Пять категорий типов определяются пользователем: типы классов, типы структур, типы интерфейсов, перечисляемые типы и типы делегатов.Five of C#'s categories of types are user-definable: class types, struct types, interface types, enum types, and delegate types.

Тип класса определяет структуру данных, содержащую элементы данных (поля) и функции-члены (методы, свойства и т. д.).A class type defines a data structure that contains data members (fields) and function members (methods, properties, and others). Классы поддерживают механизмы одиночного наследования и полиморфизма, которые позволяют создавать производные классы, расширяющие и уточняющие определения базовых классов.Class types support single inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize base classes.

Тип структуры аналогичен типу класса в том, что он представляет структуру с элементами данных и членами функций.A struct type is similar to a class type in that it represents a structure with data members and function members. Однако, в отличие от классов, структуры являются типами значений и не требует выделения кучи.However, unlike classes, structs are value types and do not require heap allocation. Типы структуры не поддерживают определяемое пользователем наследование, и все типы структуры неявно наследуют от типа object.Struct types do not support user-specified inheritance, and all struct types implicitly inherit from type object.

Тип интерфейса определяет контракт как именованный набор открытых членов-функций.An interface type defines a contract as a named set of public function members. Класс или структура, реализующие интерфейс, должны предоставлять реализации членов функций интерфейса.A class or struct that implements an interface must provide implementations of the interface's function members. Интерфейс может наследовать от нескольких базовых интерфейсов, а класс или структура могут реализовывать несколько интерфейсов.An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces.

Тип делегата представляет ссылки на методы с определенным списком параметров и типом возвращаемого значения.A delegate type represents references to methods with a particular parameter list and return type. Делегаты позволяют использовать методы как сущности, сохраняя их в переменные и передавая в качестве параметров.Delegates make it possible to treat methods as entities that can be assigned to variables and passed as parameters. Принцип работы делегатов близок к указателям функций из некоторых языков, но в отличие от указателей функций делегаты являются объектно-ориентированными и строго типизированными.Delegates are similar to the concept of function pointers found in some other languages, but unlike function pointers, delegates are object-oriented and type-safe.

Типы классов, структур, интерфейсов и делегатов поддерживают универсальные шаблоны, что позволяет использовать их для параметризации с другими типами.Class, struct, interface and delegate types all support generics, whereby they can be parameterized with other types.

Тип перечисления — это отдельный тип с именованными константами.An enum type is a distinct type with named constants. Каждый тип перечисления имеет базовый тип, который должен быть одним из восьми целочисленных типов.Every enum type has an underlying type, which must be one of the eight integral types. Набор значений перечисляемого типа совпадает с набором значений базового типа.The set of values of an enum type is the same as the set of values of the underlying type.

C# поддерживает одно- и многомерные массивы любого типа.C# supports single- and multi-dimensional arrays of any type. В отличие от перечисленных выше типов, типы массивов не требуется объявлять перед использованием.Unlike the types listed above, array types do not have to be declared before they can be used. Типы массивов можно сформировать, просто введя квадратные скобки после имени типа.Instead, array types are constructed by following a type name with square brackets. Например, int[] является одномерным int int[,] intмассивом, является двумерным массивом, и int[][] является одномерным массивом одномерных массивов int.For example, int[] is a single-dimensional array of int, int[,] is a two-dimensional array of int, and int[][] is a single-dimensional array of single-dimensional arrays of int.

Типы, допускающие значение null, также не нужно объявлять, прежде чем их можно будет использовать.Nullable types also do not have to be declared before they can be used. Для каждого типа T значения, не допускающего значения NULL, существует соответствующий T?тип, допускающий значение null, nullкоторый может содержать дополнительное значение.For each non-nullable value type T there is a corresponding nullable type T?, which can hold an additional value null. Например, int? — это тип, который может содержать любое 32-разрядное целое nullчисло или значение.For instance, int? is a type that can hold any 32 bit integer or the value null.

C#является унифицированной, так что значение любого типа может рассматриваться как объект.C#'s type system is unified such that a value of any type can be treated as an object. Каждый тип в C# является прямо или косвенно производным от типа класса object, и этот тип object является исходным базовым классом для всех типов.Every type in C# directly or indirectly derives from the object class type, and object is the ultimate base class of all types. Чтобы значения ссылочного типа обрабатывались как объекты, им просто присваивается тип object.Values of reference types are treated as objects simply by viewing the values as type object. Значения типов значений обрабатываются как объекты путем выполнения операций упаковки и распаковки .Values of value types are treated as objects by performing boxing and unboxing operations. В следующем примере значение int преобразуется в object, а затем обратно в int.In the following example, an int value is converted to object and back again to int.

using System;

class Test
{
    static void Main() {
        int i = 123;
        object o = i;          // Boxing
        int j = (int)o;        // Unboxing
    }
}

При преобразовании значения типа значения в тип object, экземпляр объекта, также называемый "Box", выделяется для хранения значения, а значение копируется в это поле.When a value of a value type is converted to type object, an object instance, also called a "box," is allocated to hold the value, and the value is copied into that box. И наоборот, если object ссылка приводится к типу значения, выполняется проверка того, что объект, на который указывает ссылка, является полем правильного типа значения, и, если проверка прошла, то значение в поле копируется.Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.

C#— это фактические типы значений, которые могут стать объектами "по запросу".C#'s unified type system effectively means that value types can become objects "on demand." Такая унификация позволяет применять универсальные библиотеки, использующие тип object, как со ссылочными типами, так и с типами значений.Because of the unification, general-purpose libraries that use type object can be used with both reference types and value types.

В C# существует несколько типов переменных, в том числе поля, элементы массива, локальные переменные и параметры.There are several kinds of variables in C#, including fields, array elements, local variables, and parameters. Переменные представляют места хранения, и каждая переменная имеет тип, который определяет, какие значения могут храниться в переменной, как показано в следующей таблице.Variables represent storage locations, and every variable has a type that determines what values can be stored in the variable, as shown by the following table.

Тип переменнойType of Variable Возможное содержимоеPossible Contents
Тип значения, не допускающий значения NullNon-nullable value type Значение такого типаA value of that exact type
Тип значения, допускающий значение NullNullable value type Значение null или значение этого типаA null value or a value of that exact type
object Пустая ссылка, ссылка на объект любого ссылочного типа или ссылка на упакованное значение любого типа значенияA null reference, a reference to an object of any reference type, or a reference to a boxed value of any value type
Тип классаClass type Пустая ссылка, ссылка на экземпляр этого типа класса или ссылка на экземпляр класса, производного от этого типа классаA null reference, a reference to an instance of that class type, or a reference to an instance of a class derived from that class type
Тип интерфейсаInterface type Пустая ссылка, ссылка на экземпляр типа класса, который реализует этот тип интерфейса, или ссылка на упакованное значение типа значения, реализующего этот тип интерфейсаA null reference, a reference to an instance of a class type that implements that interface type, or a reference to a boxed value of a value type that implements that interface type
Тип массиваArray type Пустая ссылка, ссылка на экземпляр этого типа массива или ссылка на экземпляр совместимого типа массиваA null reference, a reference to an instance of that array type, or a reference to an instance of a compatible array type
Тип делегатаDelegate type Пустая ссылка или ссылка на экземпляр этого типа делегатаA null reference or a reference to an instance of that delegate type

ВыраженияExpressions

Выражения создаются из операндов и операторов.Expressions are constructed from operands and operators. Операторы в выражении указывают, какие действия нужно применить к операндам.The operators of an expression indicate which operations to apply to the operands. Примеры операторов: +, -, *, / и new.Examples of operators include +, -, *, /, and new. Операндами могут являться литералы, поля, локальные переменные, выражения и т. п.Examples of operands include literals, fields, local variables, and expressions.

Если выражение содержит несколько операторов, порядок вычисления этих операторов определяется их приоритетом.When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. Например, выражение x + y * z вычисляется как x + (y * z), поскольку оператор * имеет более высокий приоритет, чем оператор +.For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than the + operator.

Большинство операторов допускают перегрузку.Most operators can be overloaded. Перегрузка операторов позволяет создать пользовательскую реализацию оператора для таких операций, в которых один или оба операнда имеют определяемый пользователем тип класса или структуры.Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type.

В следующей таблице перечислены C#операторы, в которых перечислены категории операторов в порядке приоритета от самого высокого до самого низкого.The following table summarizes C#'s operators, listing the operator categories in order of precedence from highest to lowest. Операторы в одной категории имеют одинаковый приоритет.Operators in the same category have equal precedence.

КатегорияCategory ВыражениеExpression ОписаниеDescription
ПервичныйPrimary x.m Доступ к членамMember access
x(...) Вызов метода и делегатаMethod and delegate invocation
x[...] Доступ к массиву и индексаторуArray and indexer access
x++ Постфиксный инкрементPost-increment
x-- Постфиксный декрементPost-decrement
new T(...) Создание объекта и делегатаObject and delegate creation
new T(...){...} создание объекта с инициализаторомObject creation with initializer
new {...} анонимный инициализатор объектаAnonymous object initializer
new T[...] создание массиваArray creation
typeof(T) получение объекта System.Type для TObtain System.Type object for T
checked(x) Вычисление выражения в проверенном контекстеEvaluate expression in checked context
unchecked(x) Вычисление выражения в непроверенном контекстеEvaluate expression in unchecked context
default(T) получение значения по умолчанию для типа TObtain default value of type T
delegate {...} Анонимная функция (анонимный метод)Anonymous function (anonymous method)
УнарныйUnary +x идентификацииIdentity
-x ОтрицаниеNegation
!x Логическое отрицаниеLogical negation
~x Поразрядное отрицаниеBitwise negation
++x Префиксный инкрементPre-increment
--x Префиксный декрементPre-decrement
(T)x явное преобразование x в тип TExplicitly convert x to type T
await x асинхронное ожидание завершения xAsynchronously wait for x to complete
МультипликативныйMultiplicative x * y УмножениеMultiplication
x / y ДелениеDivision
x % y ОстатокRemainder
АддитивныйAdditive x + y Сложение, объединение строк, объединение делегатовAddition, string concatenation, delegate combination
x - y Вычитание, удаление делегатовSubtraction, delegate removal
СдвигShift x << y Сдвиг влевоShift left
x >> y Сдвиг вправоShift right
Тестирования типа и относительныеRelational and type testing x < y МеньшеLess than
x > y БольшеGreater than
x <= y Меньше или равноLess than or equal
x >= y Больше или равноGreater than or equal
x is T возвращает true, если x является T, или false в противном случаеReturn true if x is a T, false otherwise
x as T возвращает x с типом T или null, если x не является TReturn x typed as T, or null if x is not a T
РавенствоEquality x == y РавноEqual
x != y Не равноNot equal
Логическое ИLogical AND x & y поразрядное логическое И для операндов целочисленного типа, логическое И для операндов логического типаInteger bitwise AND, boolean logical AND
Логическое исключающее ИЛИLogical XOR x ^ y Поразрядное исключающее ИЛИ для операндов целочисленного типа, логическое исключающее ИЛИ для операндов логического типаInteger bitwise XOR, boolean logical XOR
Логическое ИЛИLogical OR x | y Поразрядное ИЛИ для операндов целочисленного типа, логическое ИЛИ для операндов логического типаInteger bitwise OR, boolean logical OR
Условное ИConditional AND x && y Вычисляет, x только если имеет значение y``trueEvaluates y only if x is true
Условное ИЛИConditional OR x || y Вычисляет, x только если имеет значение y``falseEvaluates y only if x is false
Объединение со значением NULLNull coalescing x ?? y Принимает значение, x nullесли имеет значение, x в противном случае yEvaluates to y if x is null, to x otherwise
УсловиеConditional x ? y : z возвращает y, если x имеет значение true, или z, если x имеет значение falseEvaluates y if x is true, z if x is false
Присваивание или анонимная функцияAssignment or anonymous function x = y НазначениеAssignment
x op= y Составное назначение; Поддерживаемые операторы *= /= %= += -= <<= >>= &= ^=|=Compound assignment; supported operators are *= /= %= += -= <<= >>= &= ^= |=
(T x) => y Анонимная функция (лямбда-выражение)Anonymous function (lambda expression)

ОператорыStatements

Действия программы выражаются с помощью операторов.The actions of a program are expressed using statements. C# поддерживает несколько типов операторов, некоторые из которых определяются как внедренные операторы.C# supports several different kinds of statements, a number of which are defined in terms of embedded statements.

С помощью блоков можно использовать несколько операторов в таких контекстах, где ожидается только один оператор.A block permits multiple statements to be written in contexts where a single statement is allowed. Блок состоит из списка инструкций, заключенных между разделителями { и }.A block consists of a list of statements written between the delimiters { and }.

Операторы объявления используются для объявления локальных переменных и констант.Declaration statements are used to declare local variables and constants.

Операторы выражений позволяют вычислять выражения.Expression statements are used to evaluate expressions. Выражения, которые могут использоваться в качестве инструкций, включают в себя вызовы методов, выделения объектов с new помощью оператора, назначения = с помощью и составных операторов присваивания, увеличения и уменьшения с помощью ++операторы -- and и await Expressions.Expressions that can be used as statements include method invocations, object allocations using the new operator, assignments using = and the compound assignment operators, increment and decrement operations using the ++ and -- operators and await expressions.

Операторы выбора используются для выбора одного оператора из нескольких возможных вариантов в зависимости от значения какого-либо выражения.Selection statements are used to select one of a number of possible statements for execution based on the value of some expression. К этой группе относятся операторы if и switch.In this group are the if and switch statements.

Операторы итерации используются для многократного выполнения внедренного оператора.Iteration statements are used to repeatedly execute an embedded statement. К этой группе относятся операторы while, do, for и foreach.In this group are the while, do, for, and foreach statements.

Операторы перехода используются для передачи управления.Jump statements are used to transfer control. К этой группе относятся операторы break, continue, goto, throw, return и yield.In this group are the break, continue, goto, throw, return, and yield statements.

Операторы try...catch позволяют перехватывать исключения, создаваемые при выполнении блока кода, а оператор try...finally используется для указания кода завершения, который выполняется всегда, независимо от появления исключений.The try...catch statement is used to catch exceptions that occur during execution of a block, and the try...finally statement is used to specify finalization code that is always executed, whether an exception occurred or not.

Операторы checked иunchecked используются для управления контекстом проверки переполнения для арифметических операций и преобразований целочисленного типа.The checked and unchecked statements are used to control the overflow checking context for integral-type arithmetic operations and conversions.

Оператор lock позволяет создать взаимоисключающую блокировку заданного объекта перед выполнением определенных операторов, а затем снять блокировку.The lock statement is used to obtain the mutual-exclusion lock for a given object, execute a statement, and then release the lock.

Оператор using используется для получения ресурса перед определенным оператором, и для удаления ресурса после его завершения.The using statement is used to obtain a resource, execute a statement, and then dispose of that resource.

Ниже приведены примеры всех видов операторов.Below are examples of each kind of statement

Объявления локальных переменныхLocal variable declarations

static void Main() {
   int a;
   int b = 2, c = 3;
   a = 1;
   Console.WriteLine(a + b + c);
}

Объявление локальной константыLocal constant declaration

static void Main() {
    const float pi = 3.1415927f;
    const int r = 25;
    Console.WriteLine(pi * r * r);
}

Оператор выраженияExpression statement

static void Main() {
    int i;
    i = 123;                // Expression statement
    Console.WriteLine(i);   // Expression statement
    i++;                    // Expression statement
    Console.WriteLine(i);   // Expression statement
}

Инструкция ifif statement

static void Main(string[] args) {
    if (args.Length == 0) {
        Console.WriteLine("No arguments");
    }
    else {
        Console.WriteLine("One or more arguments");
    }
}

Инструкция switchswitch statement

static void Main(string[] args) {
    int n = args.Length;
    switch (n) {
        case 0:
            Console.WriteLine("No arguments");
            break;
        case 1:
            Console.WriteLine("One argument");
            break;
        default:
            Console.WriteLine("{0} arguments", n);
            break;
    }
}

Инструкция whilewhile statement

static void Main(string[] args) {
    int i = 0;
    while (i < args.Length) {
        Console.WriteLine(args[i]);
        i++;
    }
}

Инструкция dodo statement

static void Main() {
    string s;
    do {
        s = Console.ReadLine();
        if (s != null) Console.WriteLine(s);
    } while (s != null);
}

Инструкция forfor statement

static void Main(string[] args) {
    for (int i = 0; i < args.Length; i++) {
        Console.WriteLine(args[i]);
    }
}

Инструкция foreachforeach statement

static void Main(string[] args) {
    foreach (string s in args) {
        Console.WriteLine(s);
    }
}

Инструкция breakbreak statement

static void Main() {
    while (true) {
        string s = Console.ReadLine();
        if (s == null) break;
        Console.WriteLine(s);
    }
}

Инструкция continuecontinue statement

static void Main(string[] args) {
    for (int i = 0; i < args.Length; i++) {
        if (args[i].StartsWith("/")) continue;
        Console.WriteLine(args[i]);
    }
}

Инструкция gotogoto statement

static void Main(string[] args) {
    int i = 0;
    goto check;
    loop:
    Console.WriteLine(args[i++]);
    check:
    if (i < args.Length) goto loop;
}

Инструкция returnreturn statement

static int Add(int a, int b) {
    return a + b;
}

static void Main() {
    Console.WriteLine(Add(1, 2));
    return;
}

Инструкция yieldyield statement

static IEnumerable<int> Range(int from, int to) {
    for (int i = from; i < to; i++) {
        yield return i;
    }
    yield break;
}

static void Main() {
    foreach (int x in Range(-10,10)) {
        Console.WriteLine(x);
    }
}

throwоператоры try иthrow and try statements

static double Divide(double x, double y) {
    if (y == 0) throw new DivideByZeroException();
    return x / y;
}

static void Main(string[] args) {
    try {
        if (args.Length != 2) {
            throw new Exception("Two numbers required");
        }
        double x = double.Parse(args[0]);
        double y = double.Parse(args[1]);
        Console.WriteLine(Divide(x, y));
    }
    catch (Exception e) {
        Console.WriteLine(e.Message);
    }
    finally {
        Console.WriteLine("Good bye!");
    }
}

checkedоператоры unchecked иchecked and unchecked statements

static void Main() {
    int i = int.MaxValue;
    checked {
        Console.WriteLine(i + 1);        // Exception
    }
    unchecked {
        Console.WriteLine(i + 1);        // Overflow
    }
}

Инструкция locklock statement

class Account
{
    decimal balance;
    public void Withdraw(decimal amount) {
        lock (this) {
            if (amount > balance) {
                throw new Exception("Insufficient funds");
            }
            balance -= amount;
        }
    }
}

Инструкция usingusing statement

static void Main() {
    using (TextWriter w = File.CreateText("test.txt")) {
        w.WriteLine("Line one");
        w.WriteLine("Line two");
        w.WriteLine("Line three");
    }
}

Классы и объектыClasses and objects

Классы являются самым важным типом в языке C#.Classes are the most fundamental of C#'s types. Класс представляет собой структуру данных, которая объединяет в себе значения (поля) и действия (методы и другие функции-члены).A class is a data structure that combines state (fields) and actions (methods and other function members) in a single unit. Класс предоставляет определение для динамически создаваемых экземпляров класса, которые также именуются объектами.A class provides a definition for dynamically created instances of the class, also known as objects. Классы поддерживают механизмы наследования и полиморфизма, которые позволяют создавать производные классы, расширяющие и уточняющие определения базовых классов.Classes support inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize base classes.

Новые классы создаются с помощью объявлений классов.New classes are created using class declarations. Объявление класса начинается с заголовка, в котором указаны атрибуты и модификаторы класса, имя класса, базовый класс (если есть) и интерфейсы, реализуемые этим классом.A class declaration starts with a header that specifies the attributes and modifiers of the class, the name of the class, the base class (if given), and the interfaces implemented by the class. За заголовком между разделителями { и } следует тело класса, в котором последовательно объявляются все члены класса.The header is followed by the class body, which consists of a list of member declarations written between the delimiters { and }.

Вот простой пример объявления класса с именем Point:The following is a declaration of a simple class named Point:

public class Point
{
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Экземпляры классов создаются с помощью оператора new, который выделяет память для нового экземпляра, вызывает конструктор для инициализации этого экземпляра и возвращает ссылку на экземпляр.Instances of classes are created using the new operator, which allocates memory for a new instance, invokes a constructor to initialize the instance, and returns a reference to the instance. Следующие инструкции создают два Point объекта и сохраняют ссылки на эти объекты в двух переменных:The following statements create two Point objects and store references to those objects in two variables:

Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);

Память, занятая объектом, автоматически освобождается, когда объект больше не используется.The memory occupied by an object is automatically reclaimed when the object is no longer in use. В C# нет ни необходимости, ни возможности освобождать память объектов явным образом.It is neither necessary nor possible to explicitly deallocate objects in C#.

УчастникиMembers

Члены класса являются статическими элементами или членами экземпляра.The members of a class are either static members or instance members. Статические члены принадлежат классу в целом, а члены экземпляра принадлежат конкретным объектам (экземплярам классов).Static members belong to classes, and instance members belong to objects (instances of classes).

В следующей таблице приведены общие сведения о типах элементов, которые может содержать класс.The following table provides an overview of the kinds of members a class can contain.

УчастникамиMember ОписаниеDescription
КонстантыConstants Константные значения, связанные с классом.Constant values associated with the class
ПоляFields Переменные класса.Variables of the class
МетодыMethods Вычисления и действия, которые может выполнять класс.Computations and actions that can be performed by the class
СвойстваProperties Действия, связанные с чтением и записью именованных свойств класса.Actions associated with reading and writing named properties of the class
ИндексаторыIndexers Действия, реализующие индексирование экземпляров класса, чтобы обращаться к ним как к массиву.Actions associated with indexing instances of the class like an array
СобытияEvents Уведомления, которые могут быть созданы этим классом.Notifications that can be generated by the class
ОператорыOperators Поддерживаемые классом операторы преобразования и выражения.Conversions and expression operators supported by the class
КонструкторыConstructors Действия, необходимые для инициализации экземпляров класса или класса в целом.Actions required to initialize instances of the class or the class itself
ДеструкторыDestructors Действия, выполняемые перед окончательным удалением экземпляров класса.Actions to perform before instances of the class are permanently discarded
ТипыTypes Вложенные типы, объявленные в классе.Nested types declared by the class

Специальные возможностиAccessibility

Каждый член класса имеет определенный уровень доступности. Он определяет, из какой области программы можно обращаться к этому члену.Each member of a class has an associated accessibility, which controls the regions of program text that are able to access the member. Существует пять уровней доступности.There are five possible forms of accessibility. Эти отчеты представлены в следующей таблице.These are summarized in the following table.

Специальные возможностиAccessibility АMeaning
public Доступ не ограничен.Access not limited
protected Доступ возможен из этого класса и из классов, унаследованных от него.Access limited to this class or classes derived from this class
internal Доступ возможен только из текущей программы.Access limited to this program
protected internal Доступ возможен только из текущей программы и из классов, унаследованных от этого класса.Access limited to this program or classes derived from this class
private Доступ возможен только из этого класса.Access limited to this class

Параметры типаType parameters

Определение класса может задать набор параметров типа. Список имен параметров типа указывается в угловых скобках после имени класса.A class definition may specify a set of type parameters by following the class name with angle brackets enclosing a list of type parameter names. Параметры типа можно использовать в теле объявлений класса для определения членов класса.The type parameters can the be used in the body of the class declarations to define the members of the class. В следующем примере для класса Pair заданы параметры типа TFirst и TSecond:In the following example, the type parameters of Pair are TFirst and TSecond:

public class Pair<TFirst,TSecond>
{
    public TFirst First;
    public TSecond Second;
}

Тип класса, объявленный для получения параметров типа, называется универсальным типом класса.A class type that is declared to take type parameters is called a generic class type. Типы структуры, интерфейса и делегата также могут быть универсальными.Struct, interface and delegate types can also be generic.

Если вы используете универсальный класс, необходимо указать аргумент типа для каждого параметра типа, вот так:When the generic class is used, type arguments must be provided for each of the type parameters:

Pair<int,string> pair = new Pair<int,string> { First = 1, Second = "two" };
int i = pair.First;     // TFirst is int
string s = pair.Second; // TSecond is string

Универсальный тип с указанными аргументами-типами Pair<int,string> , как и выше, называется сконструированным типом.A generic type with type arguments provided, like Pair<int,string> above, is called a constructed type.

базовых классов;Base classes

В объявлении класса можно указать базовый класс, включив имя базового класса после имени класса и параметров типа, и отделив его двоеточием.A class declaration may specify a base class by following the class name and type parameters with a colon and the name of the base class. Если спецификация базового класса не указана, класс наследуется от типа object.Omitting a base class specification is the same as deriving from type object. В следующем примере Point3D имеет базовый класс Point, а Point — базовый класс object:In the following example, the base class of Point3D is Point, and the base class of Point is object:

public class Point
{
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class Point3D: Point
{
    public int z;

    public Point3D(int x, int y, int z): base(x, y) {
        this.z = z;
    }
}

Класс наследует члены базового класса.A class inherits the members of its base class. Наследование означает, что класс неявно содержит все члены базового класса, за исключением экземпляров и статических конструкторов, а также деструкторов базового класса.Inheritance means that a class implicitly contains all members of its base class, except for the instance and static constructors, and the destructors of the base class. Производный класс может дополнить наследуемые элементы новыми элементами, но он не может удалить определение для наследуемого члена.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member. В предыдущем примере Point3D наследует поля x и y из Point, и каждый экземпляр Point3D содержит три поля: x, y и z.In the previous example, Point3D inherits the x and y fields from Point, and every Point3D instance contains three fields, x, y, and z.

Используется неявное преобразование из типа класса к любому из типов соответствующего базового класса.An implicit conversion exists from a class type to any of its base class types. Это означает, что переменная типа класса может ссылаться как на экземпляр этого класса, так и на экземпляры любого производного класса.Therefore, a variable of a class type can reference an instance of that class or an instance of any derived class. Например, если мы используем описанные выше объявления классов, то переменная типа Point может ссылаться на Point или Point3D:For example, given the previous class declarations, a variable of type Point can reference either a Point or a Point3D:

Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);

ПоляFields

Поле — это переменная, связанная с классом или с экземпляром класса.A field is a variable that is associated with a class or with an instance of a class.

Поле, объявленное с static модификатором, определяет статическое поле.A field declared with the static modifier defines a static field. Статическое поле определяет строго одно место хранения.A static field identifies exactly one storage location. Независимо от того, сколько будет создано экземпляров этого класса, существует только одна копия статического поля.No matter how many instances of a class are created, there is only ever one copy of a static field.

Поле, объявленное без static модификатора, определяет поле экземпляра.A field declared without the static modifier defines an instance field. Каждый экземпляр класса содержит отдельные копии всех полей экземпляра, определенных для этого класса.Every instance of a class contains a separate copy of all the instance fields of that class.

В следующем примере каждый экземпляр класса Color содержит отдельную копию полей экземпляра r, g и b, но для каждого из статических полей Black, White, Red, Green и Blue существует только одна копия:In the following example, each instance of the Color class has a separate copy of the r, g, and b instance fields, but there is only one copy of the Black, White, Red, Green, and Blue static fields:

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte r, g, b;

    public Color(byte r, byte g, byte b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }
}

Как показано в предыдущем примере, можно объявить поля только для чтения, используя модификатор readonly.As shown in the previous example, read-only fields may be declared with a readonly modifier. Присваивание readonly полю может выполняться только как часть объявления поля или в конструкторе в том же классе.Assignment to a readonly field can only occur as part of the field's declaration or in a constructor in the same class.

МетодыMethods

Метод — это член, реализующий вычисление или действие, которое может выполнять объект или класс.A method is a member that implements a computation or action that can be performed by an object or class. Доступ к статическим методам осуществляется через класс.Static methods are accessed through the class. Доступ к методам экземпляра осуществляется через экземпляр класса.Instance methods are accessed through instances of the class.

Методы имеют (возможно, пустой) список параметров, которые представляют значения или ссылки на переменные, передаваемые в метод, и тип возвращаемогозначения, который указывает тип значений, вычисленных и возвращаемых методом.Methods have a (possibly empty) list of parameters, which represent values or variable references passed to the method, and a return type, which specifies the type of the value computed and returned by the method. Тип возвращаемого значения метода — void , если он не возвращает значение.A method's return type is void if it does not return a value.

Как и типы, методы могут иметь набор параметров типа, для которых при вызове метода необходимо указывать аргументы типа.Like types, methods may also have a set of type parameters, for which type arguments must be specified when the method is called. В отличие от типов, аргументы типа зачастую могут выводиться из аргументов вызова метода, и тогда их не обязательно задавать явным образом.Unlike types, the type arguments can often be inferred from the arguments of a method call and need not be explicitly given.

Сигнатура метода должна быть уникальной в пределах класса, в котором объявлен этот метод.The signature of a method must be unique in the class in which the method is declared. Сигнатура метода включает имя метода, количество параметров типа, а также количество, модификаторы и типы параметров метода.The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. Сигнатура метода не включает возвращаемый тип.The signature of a method does not include the return type.

ПараметрыParameters

Параметры позволяют передать в метод значения или ссылки на переменные.Parameters are used to pass values or variable references to methods. Фактические значения параметрам метода присваиваются на основе аргументов, заданных при вызове метода.The parameters of a method get their actual values from the arguments that are specified when the method is invoked. Существует четыре типа параметров: параметры значения, ссылочные параметры, параметры вывода и массивы параметров.There are four kinds of parameters: value parameters, reference parameters, output parameters, and parameter arrays.

Параметр значения передает один входной параметр.A value parameter is used for input parameter passing. Параметр значения сопоставляется с локальной переменной, которая получит начальное значение из значения аргумента, переданного в этом параметре.A value parameter corresponds to a local variable that gets its initial value from the argument that was passed for the parameter. Изменения параметра значения не влияют на аргумент, переданный для этого параметра.Modifications to a value parameter do not affect the argument that was passed for the parameter.

Параметры значения можно сделать необязательными, указав для них значения по умолчанию. Тогда соответствующие аргументы можно не указывать.Value parameters can be optional, by specifying a default value so that corresponding arguments can be omitted.

Ссылочный параметр используется как для входных, так и для выходных параметров.A reference parameter is used for both input and output parameter passing. Аргумент, передаваемый ссылочному параметру, должен являться переменной. При выполнении метода ссылочный параметр указывает на то же место хранения, где размещена переменная аргумента.The argument passed for a reference parameter must be a variable, and during execution of the method, the reference parameter represents the same storage location as the argument variable. Чтобы объявить ссылочный параметр, используйте модификатор ref.A reference parameter is declared with the ref modifier. Следующий пример кода демонстрирует использование параметров ref.The following example shows the use of ref parameters.

using System;

class Test
{
    static void Swap(ref int x, ref int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    static void Main() {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine("{0} {1}", i, j);            // Outputs "2 1"
    }
}

Параметр вывода передает один выходной параметр.An output parameter is used for output parameter passing. Параметр вывода действует так же, как и ссылочный параметр, но для него не используется исходное значение аргумента, предоставленного при вызове метода.An output parameter is similar to a reference parameter except that the initial value of the caller-provided argument is unimportant. Чтобы объявить параметр вывода, используйте модификатор out.An output parameter is declared with the out modifier. Следующий пример кода демонстрирует использование параметров out.The following example shows the use of out parameters.

using System;

class Test
{
    static void Divide(int x, int y, out int result, out int remainder) {
        result = x / y;
        remainder = x % y;
    }

    static void Main() {
        int res, rem;
        Divide(10, 3, out res, out rem);
        Console.WriteLine("{0} {1}", res, rem);    // Outputs "3 1"
    }
}

Массив параметров позволяет передавать в метод переменное число аргументов.A parameter array permits a variable number of arguments to be passed to a method. Чтобы объявить массив параметров, используйте модификатор params.A parameter array is declared with the params modifier. Массив параметров может быть только последним параметром в методе. Для него можно использовать только тип одномерного массива.Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type. Методы Write иWriteLine класса являются хорошим примером использования массива параметров. System.ConsoleThe Write and WriteLine methods of the System.Console class are good examples of parameter array usage. Ниже представлены объявления этих методов.They are declared as follows.

public class Console
{
    public static void Write(string fmt, params object[] args) {...}
    public static void WriteLine(string fmt, params object[] args) {...}
    ...
}

Внутри метода массив параметров полностью идентичен обычному параметру типа массив.Within a method that uses a parameter array, the parameter array behaves exactly like a regular parameter of an array type. Но зато при вызове метода, использующего массив параметров, ему можно передать либо один аргумент типа массив, либо любое количество аргументов типа элемент для массива параметров.However, in an invocation of a method with a parameter array, it is possible to pass either a single argument of the parameter array type or any number of arguments of the element type of the parameter array. В последнем случае экземпляр массива автоматически создается и инициализируется с заданными аргументами.In the latter case, an array instance is automatically created and initialized with the given arguments. Код из этого примера...This example

Console.WriteLine("x={0} y={1} z={2}", x, y, z);

...эквивалентен следующей конструкции:is equivalent to writing the following.

string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);

Тело метода и локальные переменныеMethod body and local variables

Тело метода задает инструкции, которые должны выполняться при вызове метода.A method's body specifies the statements to execute when the method is invoked.

В теле метода можно объявлять переменные, относящиеся к выполнению этого метода.A method body can declare variables that are specific to the invocation of the method. Такие переменные называются локальными переменными.Such variables are called local variables. В объявлении локальной переменной нужно указать имя типа и имя переменной. Также можно задать ее начальное значение.A local variable declaration specifies a type name, a variable name, and possibly an initial value. Следующий пример кода объявляет локальную переменную i с нулевым начальным значением, и еще одну локальную переменную j без начального значения.The following example declares a local variable i with an initial value of zero and a local variable j with no initial value.

using System;

class Squares
{
    static void Main() {
        int i = 0;
        int j;
        while (i < 10) {
            j = i * i;
            Console.WriteLine("{0} x {0} = {1}", i, j);
            i = i + 1;
        }
    }
}

C# требует, чтобы локальной переменной было явно присвоено значение, прежде чем можно будет получить это значение.C# requires a local variable to be definitely assigned before its value can be obtained. Например, если в предложенное выше объявление i не включить начальное значение, компилятор сообщит об ошибке при последующем использовании i, поскольку для i нет явно присвоенного значения.For example, if the declaration of the previous i did not include an initial value, the compiler would report an error for the subsequent usages of i because i would not be definitely assigned at those points in the program.

Метод может использовать инструкцию return, чтобы вернуть управление вызывающему объекту.A method can use return statements to return control to its caller. Если метод возвращает void, в нем нельзя использовать инструкцию return с выражением.In a method returning void, return statements cannot specify an expression. В методе, возвращающем return не-void, инструкция должна включать выражение, которое выполняет вычисление возвращаемого значения.In a method returning non-void, return statements must include an expression that computes the return value.

Статические методы и методы экземпляраStatic and instance methods

Метод, объявленный с static модификатором, является статическим методом.A method declared with a static modifier is a static method. Статический метод не работает с конкретным экземпляром и может напрямую обращаться только к статическим членам.A static method does not operate on a specific instance and can only directly access static members.

Метод, объявленный без static модификатора, является методом экземпляра.A method declared without a static modifier is an instance method. Метод экземпляра работает в определенном экземпляре и может обращаться как к статическим методам, так и к методам этого экземпляра.An instance method operates on a specific instance and can access both static and instance members. В методе можно напрямую обратиться к экземпляру, для которого этот метод был вызван, используя дескриптор this.The instance on which an instance method was invoked can be explicitly accessed as this. Использование this в статическом методе приводит к ошибке.It is an error to refer to this in a static method.

Следующий класс Entity содержит статические члены и члены экземпляра.The following Entity class has both static and instance members.

class Entity
{
    static int nextSerialNo;
    int serialNo;

    public Entity() {
        serialNo = nextSerialNo++;
    }

    public int GetSerialNo() {
        return serialNo;
    }

    public static int GetNextSerialNo() {
        return nextSerialNo;
    }

    public static void SetNextSerialNo(int value) {
        nextSerialNo = value;
    }
}

Каждый экземпляр Entity содержит серийный номер (и может содержать другие данные, которые здесь не показаны).Each Entity instance contains a serial number (and presumably some other information that is not shown here). Конструктор объекта Entity (который рассматривается как метод экземпляра) задает для нового экземпляра следующий доступный серийный номер.The Entity constructor (which is like an instance method) initializes the new instance with the next available serial number. Поскольку конструктор является членом экземпляра, он может обращаться как к полю экземпляра serialNo, так и к статическому полю nextSerialNo.Because the constructor is an instance member, it is permitted to access both the serialNo instance field and the nextSerialNo static field.

Статические методы GetNextSerialNo и SetNextSerialNo могут обращаться к статическому полю nextSerialNo, но прямое обращение из них к полю экземпляра serialNo приводит к ошибке.The GetNextSerialNo and SetNextSerialNo static methods can access the nextSerialNo static field, but it would be an error for them to directly access the serialNo instance field.

В следующем примере показано использование Entity класса.The following example shows the use of the Entity class.

using System;

class Test
{
    static void Main() {
        Entity.SetNextSerialNo(1000);
        Entity e1 = new Entity();
        Entity e2 = new Entity();
        Console.WriteLine(e1.GetSerialNo());           // Outputs "1000"
        Console.WriteLine(e2.GetSerialNo());           // Outputs "1001"
        Console.WriteLine(Entity.GetNextSerialNo());   // Outputs "1002"
    }
}

Обратите внимание, что статические методы SetNextSerialNo и GetNextSerialNo вызываются для класса, а метод экземпляра GetSerialNo вызывается для экземпляров класса.Note that the SetNextSerialNo and GetNextSerialNo static methods are invoked on the class whereas the GetSerialNo instance method is invoked on instances of the class.

Виртуальные, переопределяющие и абстрактные методыVirtual, override, and abstract methods

Если объявление метода экземпляра включает модификатор virtual, такой метод называется виртуальным методом.When an instance method declaration includes a virtual modifier, the method is said to be a virtual method. Если модификатор virtual не указан, метод считается невиртуальным методом.When no virtual modifier is present, the method is said to be a non-virtual method.

При вызове виртуального метода могут быть вызваны разные его реализации в зависимости от того, какой тип среды выполнения имеет экземпляр, для которого вызван этот метод.When a virtual method is invoked, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. При вызове невиртуального метода решающим фактором является тип во время компиляции для этого экземпляра.In a nonvirtual method invocation, the compile-time type of the instance is the determining factor.

Виртуальный метод можно переопределить в производном классе.A virtual method can be overridden in a derived class. Если объявление метода экземпляра содержит override модификатор, метод переопределяет наследуемый виртуальный метод с той же сигнатурой.When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. Изначальное объявление виртуального метода создает новый метод, а переопределение этого метода создает специализированный виртуальный метод с новой реализацией взамен унаследованного виртуального метода.Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

Абстрактный метод — это виртуальный метод без реализации.An abstract method is a virtual method with no implementation. Абстрактный метод объявляется с abstract модификатором и разрешается только в классе, который также объявлен. abstractAn abstract method is declared with the abstract modifier and is permitted only in a class that is also declared abstract. Абстрактный метод должен обязательно переопределяться в каждом производном классе, не являющемся абстрактным.An abstract method must be overridden in every non-abstract derived class.

Следующий пример кода объявляет абстрактный класс Expression, который представляет узел дерева выражений, а также три производных класса: Constant, VariableReference и Operation, которые реализуют узлы дерева выражений для констант, ссылок на переменные и арифметических операций.The following example declares an abstract class, Expression, which represents an expression tree node, and three derived classes, Constant, VariableReference, and Operation, which implement expression tree nodes for constants, variable references, and arithmetic operations. (Это похоже на, но не следует путать с типами деревьев выражений, представленными в типах деревьев выражений).(This is similar to, but not to be confused with the expression tree types introduced in Expression tree types).

using System;
using System.Collections;

public abstract class Expression
{
    public abstract double Evaluate(Hashtable vars);
}

public class Constant: Expression
{
    double value;

    public Constant(double value) {
        this.value = value;
    }

    public override double Evaluate(Hashtable vars) {
        return value;
    }
}

public class VariableReference: Expression
{
    string name;

    public VariableReference(string name) {
        this.name = name;
    }

    public override double Evaluate(Hashtable vars) {
        object value = vars[name];
        if (value == null) {
            throw new Exception("Unknown variable: " + name);
        }
        return Convert.ToDouble(value);
    }
}

public class Operation: Expression
{
    Expression left;
    char op;
    Expression right;

    public Operation(Expression left, char op, Expression right) {
        this.left = left;
        this.op = op;
        this.right = right;
    }

    public override double Evaluate(Hashtable vars) {
        double x = left.Evaluate(vars);
        double y = right.Evaluate(vars);
        switch (op) {
            case '+': return x + y;
            case '-': return x - y;
            case '*': return x * y;
            case '/': return x / y;
        }
        throw new Exception("Unknown operator");
    }
}

Четыре приведенных выше класса можно использовать для моделирования арифметических выражений.The previous four classes can be used to model arithmetic expressions. Например, с помощью экземпляров этих классов выражение x + 3 можно представить следующим образом.For example, using instances of these classes, the expression x + 3 can be represented as follows.

Expression e = new Operation(
    new VariableReference("x"),
    '+',
    new Constant(3));

Метод Evaluate экземпляра Expression вызывается для вычисления данного выражения и создает значение double.The Evaluate method of an Expression instance is invoked to evaluate the given expression and produce a double value. Метод принимает в качестве аргумента a Hashtable , который содержит имена переменных (в качестве ключей записей) и значения (в качестве значений записей).The method takes as an argument a Hashtable that contains variable names (as keys of the entries) and values (as values of the entries). Evaluate Метод является виртуальным абстрактным методом, что означает, что неабстрактные производные классы должны переопределять его, чтобы обеспечить фактическую реализацию.The Evaluate method is a virtual abstract method, meaning that non-abstract derived classes must override it to provide an actual implementation.

В Constant реализация метода Evaluate просто возвращает хранимую константу.A Constant's implementation of Evaluate simply returns the stored constant. Реализация VariableReferenceвыполняет поиск имени переменной в хэш-таблице и возвращает результирующее значение.A VariableReference's implementation looks up the variable name in the hashtable and returns the resulting value. В Operation реализация этого метода сначала вычисляет левый и правый операнды (рекурсивно вызывая их методы Evaluate), а затем выполняет предоставленную арифметическую операцию.An Operation's implementation first evaluates the left and right operands (by recursively invoking their Evaluate methods) and then performs the given arithmetic operation.

В следующей программе классы Expression используются для вычисления выражения x * (y + 2) с различными значениями x и y.The following program uses the Expression classes to evaluate the expression x * (y + 2) for different values of x and y.

using System;
using System.Collections;

class Test
{
    static void Main() {
        Expression e = new Operation(
            new VariableReference("x"),
            '*',
            new Operation(
                new VariableReference("y"),
                '+',
                new Constant(2)
            )
        );
        Hashtable vars = new Hashtable();
        vars["x"] = 3;
        vars["y"] = 5;
        Console.WriteLine(e.Evaluate(vars));        // Outputs "21"
        vars["x"] = 1.5;
        vars["y"] = 9;
        Console.WriteLine(e.Evaluate(vars));        // Outputs "16.5"
    }
}

Перегрузка методовMethod overloading

Перегрузка метода позволяет использовать в одном классе несколько методов с одинаковыми именами, если они имеют уникальные сигнатуры.Method overloading permits multiple methods in the same class to have the same name as long as they have unique signatures. Когда при компиляции встречается вызов перегруженного метода, компилятор использует принцип разрешения перегрузки, чтобы определить, какой из методов следует вызвать.When compiling an invocation of an overloaded method, the compiler uses overload resolution to determine the specific method to invoke. Разрешение перегрузки выбирает из методов тот, который лучше всего соответствует предоставленным аргументам, или возвращает ошибку, если не удается выбрать конкретный подходящий метод.Overload resolution finds the one method that best matches the arguments or reports an error if no single best match can be found. В следующем примере показано, как работает разрешение перегрузки.The following example shows overload resolution in effect. Комментарий к каждому вызову метода Main подсказывает, какой из методов будет вызван для этой строки.The comment for each invocation in the Main method shows which method is actually invoked.

class Test
{
    static void F() {
        Console.WriteLine("F()");
    }

    static void F(object x) {
        Console.WriteLine("F(object)");
    }

    static void F(int x) {
        Console.WriteLine("F(int)");
    }

    static void F(double x) {
        Console.WriteLine("F(double)");
    }

    static void F<T>(T x) {
        Console.WriteLine("F<T>(T)");
    }

    static void F(double x, double y) {
        Console.WriteLine("F(double, double)");
    }

    static void Main() {
        F();                 // Invokes F()
        F(1);                // Invokes F(int)
        F(1.0);              // Invokes F(double)
        F("abc");            // Invokes F(object)
        F((double)1);        // Invokes F(double)
        F((object)1);        // Invokes F(object)
        F<int>(1);           // Invokes F<T>(T)
        F(1, 1);             // Invokes F(double, double)
    }
}

Как видно из этого примера, вы всегда можете выбрать конкретный метод, явным образом приведя типы аргументов к соответствующим типам параметров, и (или) явно предоставив аргументы нужного типа.As shown by the example, a particular method can always be selected by explicitly casting the arguments to the exact parameter types and/or explicitly supplying type arguments.

Другие функции-членыOther function members

Все члены класса, содержащие исполняемый код, совокупно называются функции-члены.Members that contain executable code are collectively known as the function members of a class. В предыдущем разделе мы рассмотрели основные варианты методов, используемых как функции-члены.The preceding section describes methods, which are the primary kind of function members. В этом разделе описываются другие виды членов функций, поддерживаемых C#: конструкторы, свойства, индексаторы, события, операторы и деструкторы.This section describes the other kinds of function members supported by C#: constructors, properties, indexers, events, operators, and destructors.

В следующем коде показан универсальный класс с именем List<T>, который реализует увеличивающийся список объектов.The following code shows a generic class called List<T>, which implements a growable list of objects. Этот класс содержит несколько наиболее распространенных типов функций-членов.The class contains several examples of the most common kinds of function members.

public class List<T> {
    // Constant...
    const int defaultCapacity = 4;

    // Fields...
    T[] items;
    int count;

    // Constructors...
    public List(int capacity = defaultCapacity) {
        items = new T[capacity];
    }

    // Properties...
    public int Count {
        get { return count; }
    }
    public int Capacity {
        get {
            return items.Length;
        }
        set {
            if (value < count) value = count;
            if (value != items.Length) {
                T[] newItems = new T[value];
                Array.Copy(items, 0, newItems, 0, count);
                items = newItems;
            }
        }
    }

    // Indexer...
    public T this[int index] {
        get {
            return items[index];
        }
        set {
            items[index] = value;
            OnChanged();
        }
    }

    // Methods...
    public void Add(T item) {
        if (count == Capacity) Capacity = count * 2;
        items[count] = item;
        count++;
        OnChanged();
    }
    protected virtual void OnChanged() {
        if (Changed != null) Changed(this, EventArgs.Empty);
    }
    public override bool Equals(object other) {
        return Equals(this, other as List<T>);
    }
    static bool Equals(List<T> a, List<T> b) {
        if (a == null) return b == null;
        if (b == null || a.count != b.count) return false;
        for (int i = 0; i < a.count; i++) {
            if (!object.Equals(a.items[i], b.items[i])) {
                return false;
            }
        }
        return true;
    }

    // Event...
    public event EventHandler Changed;

    // Operators...
    public static bool operator ==(List<T> a, List<T> b) {
        return Equals(a, b);
    }
    public static bool operator !=(List<T> a, List<T> b) {
        return !Equals(a, b);
    }
}

КонструкторыConstructors

C# поддерживает конструкторы экземпляров и статические конструкторы.C# supports both instance and static constructors. Конструктор экземпляра является членом, который реализует действия для инициализации нового экземпляра класса.An instance constructor is a member that implements the actions required to initialize an instance of a class. Статический конструктор является членом, который реализует действия для инициализации самого класса при первоначальной его загрузке.A static constructor is a member that implements the actions required to initialize a class itself when it is first loaded.

Конструктор объявляется в виде метода без возвращаемого типа, имя которого совпадает с именем класса, в котором он определен.A constructor is declared like a method with no return type and the same name as the containing class. Если объявление конструктора содержит static модификатор, то он объявляет статический конструктор.If a constructor declaration includes a static modifier, it declares a static constructor. В противном случае это объявление считается конструктором экземпляра.Otherwise, it declares an instance constructor.

Конструкторы экземпляров могут быть перегружены.Instance constructors can be overloaded. Например, класс List<T> объявляет два конструктора экземпляра: один без параметров и один с параметром int.For example, the List<T> class declares two instance constructors, one with no parameters and one that takes an int parameter. Конструкторы экземпляров вызываются с помощью оператора new.Instance constructors are invoked using the new operator. Следующие инструкции выделяют два List<string> экземпляра с помощью каждого из конструкторов List класса.The following statements allocate two List<string> instances using each of the constructors of the List class.

List<string> list1 = new List<string>();
List<string> list2 = new List<string>(10);

В отличие от других членов конструкторы экземпляров не наследуются, и класс не имеет конструкторов экземпляров, кроме объявленных в самом этом классе.Unlike other members, instance constructors are not inherited, and a class has no instance constructors other than those actually declared in the class. Если в классе не объявлен конструктор экземпляра, для него автоматически создается пустой конструктор без параметров.If no instance constructor is supplied for a class, then an empty one with no parameters is automatically provided.

СвойстваProperties

Свойства естественным образом дополняют поля.Properties are a natural extension of fields. И те, и другие являются именованными членами со связанными типами, и для доступа к ним используется одинаковый синтаксис.Both are named members with associated types, and the syntax for accessing fields and properties is the same. Однако свойства, в отличие от полей, не указывают места хранения.However, unlike fields, properties do not denote storage locations. Вместо этого свойства содержат методы доступа, в которых описаны инструкции для выполнения при чтении или записи значений.Instead, properties have accessors that specify the statements to be executed when their values are read or written.

Свойство объявляется как поле, за исключением того, что объявление get заканчивается методом доступа и ( set или) методом доступа, записанным { между разделителями } , и не заканчивается точкой с запятой.A property is declared like a field, except that the declaration ends with a get accessor and/or a set accessor written between the delimiters { and } instead of ending in a semicolon. Свойство, имеющее get методы доступа set и метод доступа get , — это свойство для чтения и записи, свойство, имеющее только метод доступа, является свойством только для чтения, а свойство, имеющее set только метод доступа, — это свойство только для записи.A property that has both a get accessor and a set accessor is a read-write property, a property that has only a get accessor is a read-only property, and a property that has only a set accessor is a write-only property.

Метод get доступа соответствует не имеющему параметров, а возвращаемому значению типа свойства.A get accessor corresponds to a parameterless method with a return value of the property type. За исключением целей назначения, если в выражении имеется ссылка на свойство, get для расчета значения свойства вызывается метод доступа свойства.Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property.

Метод доступа соотносится с методом с одним параметром с именем value и без типа возвращаемого значения. setA set accessor corresponds to a method with a single parameter named value and no return type. Если на свойство имеется ссылка как на назначение или как на операнд ++ или --, set метод доступа вызывается с аргументом, предоставляющим новое значение.When a property is referenced as the target of an assignment or as the operand of ++ or --, the set accessor is invoked with an argument that provides the new value.

Класс List<T> объявляет два свойства: Count (только для чтения) и Capacity (только для записи).The List<T> class declares two properties, Count and Capacity, which are read-only and read-write, respectively. Образец использования этих свойств приведен ниже.The following is an example of use of these properties.

List<string> names = new List<string>();
names.Capacity = 100;            // Invokes set accessor
int i = names.Count;             // Invokes get accessor
int j = names.Capacity;          // Invokes get accessor

Как и в отношении полей и методов, C# поддерживает свойства экземпляра и статические свойства.Similar to fields and methods, C# supports both instance properties and static properties. Статические свойства объявляются с static помощью модификатора, а свойства экземпляра объявляются без него.Static properties are declared with the static modifier, and instance properties are declared without it.

Акцессоры свойства могут быть виртуальными.The accessor(s) of a property can be virtual. Если объявление свойства содержит модификатор virtual, abstract или override, этот модификатор применяется к акцессорам свойства.When a property declaration includes a virtual, abstract, or override modifier, it applies to the accessor(s) of the property.

ИндексаторыIndexers

Индексатор является членом, позволяющим индексировать объекты так, как будто они включены в массив.An indexer is a member that enables objects to be indexed in the same way as an array. Индексатор объявляется так же, как свойство, за исключением того, что именем элемента является this, а за этим именем следует список параметров, находящийся между разделителями [ и ].An indexer is declared like a property except that the name of the member is this followed by a parameter list written between the delimiters [ and ]. Эти параметры доступны в акцессорах индексатора.The parameters are available in the accessor(s) of the indexer. Как и свойства, можно объявить индексаторы для чтения и записи, только для чтения или только для записи. Кроме того, поддерживаются виртуальные акцессоры индексатора.Similar to properties, indexers can be read-write, read-only, and write-only, and the accessor(s) of an indexer can be virtual.

Класс List объявляет один индексатор для чтения и записи, который принимает параметр int.The List class declares a single read-write indexer that takes an int parameter. Индексатор позволяет индексировать экземпляры List значениями с типом int.The indexer makes it possible to index List instances with int values. ПримерFor example

List<string> names = new List<string>();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++) {
    string s = names[i];
    names[i] = s.ToUpper();
}

Индексаторы можно перегружать, то есть в одном классе можно объявить несколько индексаторов, если у них различаются количество или типы параметров.Indexers can be overloaded, meaning that a class can declare multiple indexers as long as the number or types of their parameters differ.

СобытияEvents

Событие — это член, с помощью которого класс или объект предоставляют уведомления.An event is a member that enables a class or object to provide notifications. Событие объявляется как поле, за исключением того, что объявление event включает ключевое слово, а тип должен быть типом делегата.An event is declared like a field except that the declaration includes an event keyword and the type must be a delegate type.

В классе, который объявляет член события, это событие действует, как обычное поле с типом делегата (если это событие не является абстрактным и не объявляет акцессоры).Within a class that declares an event member, the event behaves just like a field of a delegate type (provided the event is not abstract and does not declare accessors). Это поле хранит ссылку на делегат, который представляет добавленные к событию обработчики событий.The field stores a reference to a delegate that represents the event handlers that have been added to the event. Если дескрипторы событий отсутствуют, поле имеет nullзначение.If no event handles are present, the field is null.

Класс List<T> объявляет один член события с именем Changed, который обрабатывает добавление нового элемента.The List<T> class declares a single event member called Changed, which indicates that a new item has been added to the list. Событие вызывается OnChanged виртуальным методом, который сначала проверяет, является null ли событие (это означает, что обработчики отсутствуют). ChangedThe Changed event is raised by the OnChanged virtual method, which first checks whether the event is null (meaning that no handlers are present). Концепция создания события в точности соответствует вызову делегата, представленного этим событием. Это позволяет обойтись без особой языковой конструкции для создания событий.The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.

Клиенты реагируют на события посредством обработчиков событий.Clients react to events through event handlers. Обработчики событий можно подключать с помощью оператора += и удалять с помощью оператора -=.Event handlers are attached using the += operator and removed using the -= operator. Следующий пример кода подключает обработчик события Changed к событию List<string>.The following example attaches an event handler to the Changed event of a List<string>.

using System;

class Test
{
    static int changeCount;

    static void ListChanged(object sender, EventArgs e) {
        changeCount++;
    }

    static void Main() {
        List<string> names = new List<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(changeCount);        // Outputs "3"
    }
}

Для более сложных сценариев, требующих контроля над базовым хранилищем события, в объявлении события можно явным образом предоставить акцессоры add и remove. Они будут действовать примерно так же, как акцессор set для свойства.For advanced scenarios where control of the underlying storage of an event is desired, an event declaration can explicitly provide add and remove accessors, which are somewhat similar to the set accessor of a property.

ОператорыOperators

Оператор является членом, который определяет правила применения определенного выражения к экземплярам класса.An operator is a member that defines the meaning of applying a particular expression operator to instances of a class. Вы можете определить операторы трех типов: унарные операторы, двоичные операторы и операторы преобразования.Three kinds of operators can be defined: unary operators, binary operators, and conversion operators. Все операторы объявляются с модификаторами public и static.All operators must be declared as public and static.

Класс List<T> объявляет два оператора: operator== и operator!=. Это позволяет определить новое значение для выражений, которые применяют эти операторы к экземплярам List.The List<T> class declares two operators, operator== and operator!=, and thus gives new meaning to expressions that apply those operators to List instances. В частности, операторы определяют равенство двух List<T> экземпляров, сравнивая каждый из содержащихся объектов с Equals помощью их методов.Specifically, the operators define equality of two List<T> instances as comparing each of the contained objects using their Equals methods. Следующий пример кода использует оператор == для сравнения двух экземпляров List<int>.The following example uses the == operator to compare two List<int> instances.

using System;

class Test
{
    static void Main() {
        List<int> a = new List<int>();
        a.Add(1);
        a.Add(2);
        List<int> b = new List<int>();
        b.Add(1);
        b.Add(2);
        Console.WriteLine(a == b);        // Outputs "True"
        b.Add(3);
        Console.WriteLine(a == b);        // Outputs "False"
    }
}

Первый Console.WriteLine выводит True, поскольку два списка содержат одинаковое число объектов с одинаковыми значениями в том же порядке.The first Console.WriteLine outputs True because the two lists contain the same number of objects with the same values in the same order. Если бы в List<T> не было определения operator==, первый Console.WriteLine возвращал бы False, поскольку a и b указывают на различные экземпляры List<int>.Had List<T> not defined operator==, the first Console.WriteLine would have output False because a and b reference different List<int> instances.

ДеструкторыDestructors

Деструктор — это член, реализующий действия, необходимые для уничтожения экземпляра класса.A destructor is a member that implements the actions required to destruct an instance of a class. Деструкторы не могут иметь параметров, они не могут иметь модификаторы доступа и не могут вызываться явным образом.Destructors cannot have parameters, they cannot have accessibility modifiers, and they cannot be invoked explicitly. Деструктор для экземпляра вызывается автоматически во время сборки мусора.The destructor for an instance is invoked automatically during garbage collection.

Сборщик мусора допускает широкие широты при принятии решения о сборе объектов и запуске деструкторов.The garbage collector is allowed wide latitude in deciding when to collect objects and run destructors. В частности, время вызовов деструктора не детерминировано, а деструкторы могут выполняться в любом потоке.Specifically, the timing of destructor invocations is not deterministic, and destructors may be executed on any thread. По этим и другим причинам классы должны реализовывать деструкторы только в том случае, если другие решения не являются возможными.For these and other reasons, classes should implement destructors only when no other solutions are feasible.

Уничтожение объектов лучше контролировать с помощью инструкции using.The using statement provides a better approach to object destruction.

СтруктурыStructs

Как и классы, структуры — это сущности для хранения данных, которые могут содержать данные-члены и функции-члены. Но в отличие от классов, структуры являются типами значений и для них не выделяется память из кучи.Like classes, structs are data structures that can contain data members and function members, but unlike classes, structs are value types and do not require heap allocation. Переменная типа структура напрямую хранит все свои данные, а переменная типа класс хранит ссылку на динамически выделяемый объект.A variable of a struct type directly stores the data of the struct, whereas a variable of a class type stores a reference to a dynamically allocated object. Типы структуры не поддерживают определяемое пользователем наследование, и все типы структуры неявно наследуют от типа object.Struct types do not support user-specified inheritance, and all struct types implicitly inherit from type object.

Структуры особенно удобны для небольших структур данных, имеющих семантику значений.Structs are particularly useful for small data structures that have value semantics. Хорошими примерами структур можно считать комплексные числа, точки в системе координат или словари с парами ключ-значение.Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. Если использовать структуры вместо классов для небольших структур данных, можно существенно снизить количество операций по выделению памяти в приложении.The use of structs rather than classes for small data structures can make a large difference in the number of memory allocations an application performs. Например, следующая программа создает и инициализирует массив из 100 точек.For example, the following program creates and initializes an array of 100 points. Если реализовать Point как класс, создаются экземпляры для 101 отдельных объектов — один для массива и еще 100 для его элементов.With Point implemented as a class, 101 separate objects are instantiated—one for the array and one each for the 100 elements.

class Point
{
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Test
{
    static void Main() {
        Point[] points = new Point[100];
        for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
    }
}

Альтернативой является создание Point структуры.An alternative is to make Point a struct.

struct Point
{
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Теперь создается всего один объект (для массива), а экземпляры Point хранятся в стеке массива.Now, only one object is instantiated—the one for the array—and the Point instances are stored in-line in the array.

Конструктор структур вызывается оператором new, но это не приводит к выделению дополнительной памяти.Struct constructors are invoked with the new operator, but that does not imply that memory is being allocated. Вместо того, чтобы динамически выделять объект и возвращать ссылку на него, конструктор структуры возвращает само значение структуры (обычно сохраняя его во временном расположении в стеке), и затем это значение копируется туда, где оно нужно.Instead of dynamically allocating an object and returning a reference to it, a struct constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary.

При использовании классов две переменные могут ссылаться на один и тот же объект, поэтому может случиться так, что операции над одной переменной затронут объект, на который ссылается другая переменная.With classes, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. При использовании структур каждая переменная имеет собственную копию данных, и операции над одной переменной не могут затрагивать другую.With structs, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other. Например, выходные данные, созданные в следующем фрагменте кода, зависят Point от того, является ли класс или структура.For example, the output produced by the following code fragment depends on whether Point is a class or a struct.

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

Если Point является классом, выходные данные имеют 20 значение a , b поскольку и ссылаются на один и тот же объект.If Point is a class, the output is 20 because a and b reference the same object. Если Point является структурой, то выходными 10 данными является то, a b что присваивание создает копию значения, a.xи эта копия не затрагивается последующим присваиванием.If Point is a struct, the output is 10 because the assignment of a to b creates a copy of the value, and this copy is unaffected by the subsequent assignment to a.x.

Предыдущий пример демонстрирует два ограничения, действующие для структур.The previous example highlights two of the limitations of structs. Во-первых, копирование структуры целиком обычно менее эффективно, чем копирование ссылки на объект, поэтому присваивание и передача в качестве параметра потребует больше затрат для структур, чем для ссылочных типов.First, copying an entire struct is typically less efficient than copying an object reference, so assignment and value parameter passing can be more expensive with structs than with reference types. Во-вторых, вы не можете создавать ссылки на структуры (за исключением параметров ref и out), что в некоторых ситуациях мешает их применять.Second, except for ref and out parameters, it is not possible to create references to structs, which rules out their usage in a number of situations.

МассивыArrays

Массив — это структура данных, содержащая несколько переменных, доступ к которым осуществляется по вычисляемым индексам.An array is a data structure that contains a number of variables that are accessed through computed indices. Содержащиеся в массиве переменные именуются элементами этого массива. Все они имеют одинаковый тип, который называется типом элементов массива.The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the element type of the array.

Сами массивы имеют ссылочный тип, и объявление переменной массива только выделяет память для ссылки на экземпляр массива.Array types are reference types, and the declaration of an array variable simply sets aside space for a reference to an array instance. Фактические экземпляры массива создаются динамически во время выполнения с помощью new оператора.Actual array instances are created dynamically at run-time using the new operator. Операция задает длину нового экземпляра массива, который затем фиксируется в течение времени существования экземпляра. newThe new operation specifies the length of the new array instance, which is then fixed for the lifetime of the instance. Элементы массива имеют индексы в диапазоне от 0 до Length - 1.The indices of the elements of an array range from 0 to Length - 1. Оператор new автоматически инициализирует все элементы массива значением по умолчанию. Например, для всех числовых типов устанавливается нулевое значение, а для всех ссылочных типов — значение null.The new operator automatically initializes the elements of an array to their default value, which, for example, is zero for all numeric types and null for all reference types.

Следующий пример кода создает массив из int элементов, затем инициализирует этот массив и выводит содержимое массива.The following example creates an array of int elements, initializes the array, and prints out the contents of the array.

using System;

class Test
{
    static void Main() {
        int[] a = new int[10];
        for (int i = 0; i < a.Length; i++) {
            a[i] = i * i;
        }
        for (int i = 0; i < a.Length; i++) {
            Console.WriteLine("a[{0}] = {1}", i, a[i]);
        }
    }
}

Этот пример создает и использует одномерный массив.This example creates and operates on a single-dimensional array. Кроме этого, C# поддерживает многомерные массивы.C# also supports multi-dimensional arrays. Число измерений массива, которое именуется рангом для типа массива, всегда на единицу больше числа запятых, включенных в квадратные скобки типа массива.The number of dimensions of an array type, also known as the rank of the array type, is one plus the number of commas written between the square brackets of the array type. В следующем примере выделяется одномерный, двухмерный и трехмерный массив.The following example allocates a one-dimensional, a two-dimensional, and a three-dimensional array.

int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];

Массив a1 содержит 10 элементов, массив a2 — 50 элементов (10 × 5), и наконец a3 содержит 100 элементов (10 × 5 × 2).The a1 array contains 10 elements, the a2 array contains 50 (10 × 5) elements, and the a3 array contains 100 (10 × 5 × 2) elements.

Элементы массива могут иметь любой тип, в том числе тип массива.The element type of an array can be any type, including an array type. Массив с элементами типа массива иногда называют ступенчатым массивом, поскольку элементы такого массива не обязаны иметь одинаковую длину.An array with elements of an array type is sometimes called a jagged array because the lengths of the element arrays do not all have to be the same. Следующий пример создает массив массивов int.The following example allocates an array of arrays of int:

int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];

В первой строке создается массив с тремя элементами, каждый из которых имеет тип int[] и начальное значение null.The first line creates an array with three elements, each of type int[] and each with an initial value of null. В последующих строках эти три элемента инициализируются ссылками на отдельные экземпляры массивов различной длины.The subsequent lines then initialize the three elements with references to individual array instances of varying lengths.

Оператор позволяет указать начальные значения элементов массива с помощью инициализатора массива, который представляет собой список выражений, записанных между разделителями { и. } newThe new operator permits the initial values of the array elements to be specified using an array initializer, which is a list of expressions written between the delimiters { and }. Следующий пример создает и инициализирует массив int[] с тремя элементами.The following example allocates and initializes an int[] with three elements.

int[] a = new int[] {1, 2, 3};

Обратите внимание, что длина массива выводится из числа выражений между { и. }Note that the length of the array is inferred from the number of expressions between { and }. Локальные объявления переменных и полей можно сократить, поскольку тип массива не обязательно объявлять повторно.Local variable and field declarations can be shortened further such that the array type does not have to be restated.

int[] a = {1, 2, 3};

Оба указанных выше примера дают результат, эквивалентный такому объявлению.Both of the previous examples are equivalent to the following:

int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;

ИнтерфейсыInterfaces

Интерфейс определяет контракт, который может быть реализован классами и структурами.An interface defines a contract that can be implemented by classes and structs. Интерфейс может содержать методы, свойства, события и индексаторы.An interface can contain methods, properties, events, and indexers. Интерфейс не предоставляет реализацию членов, которые в нем определены. Он лишь перечисляет члены, которые должны быть определены в классах или структурах, реализующих этот интерфейс.An interface does not provide implementations of the members it defines—it merely specifies the members that must be supplied by classes or structs that implement the interface.

Интерфейсы могут применять множественное наследование.Interfaces may employ multiple inheritance. В следующем примере интерфейс IComboBox наследует одновременно от ITextBox и IListBox.In the following example, the interface IComboBox inherits from both ITextBox and IListBox.

interface IControl
{
    void Paint();
}

interface ITextBox: IControl
{
    void SetText(string text);
}

interface IListBox: IControl
{
    void SetItems(string[] items);
}

interface IComboBox: ITextBox, IListBox {}

Классы и структуры могут реализовывать несколько интерфейсов.Classes and structs can implement multiple interfaces. В следующем примере класс EditBox реализует одновременно IControl и IDataBound.In the following example, the class EditBox implements both IControl and IDataBound.

interface IDataBound
{
    void Bind(Binder b);
}

public class EditBox: IControl, IDataBound
{
    public void Paint() {...}
    public void Bind(Binder b) {...}
}

Если класс или структура реализует конкретный интерфейс, любой экземпляр этого класса или структуры можно неявно преобразовать в такой тип интерфейса.When a class or struct implements a particular interface, instances of that class or struct can be implicitly converted to that interface type. ПримерFor example

EditBox editBox = new EditBox();
IControl control = editBox;
IDataBound dataBound = editBox;

Если в статическом контексте невозможно достоверно знать, что экземпляр реализует определенный интерфейс, можно использовать динамическое приведение типов.In cases where an instance is not statically known to implement a particular interface, dynamic type casts can be used. Например, следующие инструкции используют динамическое приведение типов для получения реализаций интерфейса IControl и IDataBound объекта.For example, the following statements use dynamic type casts to obtain an object's IControl and IDataBound interface implementations. Так как фактический тип объекта —, EditBoxприведения выполняются правильно.Because the actual type of the object is EditBox, the casts succeed.

object obj = new EditBox();
IControl control = (IControl)obj;
IDataBound dataBound = (IDataBound)obj;

В EditBox предыдущем public классе Paint метод IControl IDataBound из интерфейса и методаизинтерфейсареализуютсяспомощьючленов.BindIn the previous EditBox class, the Paint method from the IControl interface and the Bind method from the IDataBound interface are implemented using public members. C#также поддерживает явные реализации членов интерфейса, с помощью которых класс или структура могут избежать создания элементов public.C# also supports explicit interface member implementations, using which the class or struct can avoid making the members public. Явная реализация члена интерфейса записывается с использованием полного имени члена интерфейса.An explicit interface member implementation is written using the fully qualified interface member name. Например, класс EditBox может реализовывать методы IControl.Paint и IDataBound.Bind с использованием явной реализации членов интерфейса, как показано в следующем примере.For example, the EditBox class could implement the IControl.Paint and IDataBound.Bind methods using explicit interface member implementations as follows.

public class EditBox: IControl, IDataBound
{
    void IControl.Paint() {...}
    void IDataBound.Bind(Binder b) {...}
}

Обращение к явным членам интерфейса можно осуществлять только через тип интерфейса.Explicit interface members can only be accessed via the interface type. Например, реализация IControl.Paint , предоставляемая предыдущим EditBox классом, может быть вызвана только путем предварительного IControl преобразования EditBox ссылки в тип интерфейса.For example, the implementation of IControl.Paint provided by the previous EditBox class can only be invoked by first converting the EditBox reference to the IControl interface type.

EditBox editBox = new EditBox();
editBox.Paint();                        // Error, no such method
IControl control = editBox;
control.Paint();                        // Ok

перечислениям;Enums

Тип enum представляет собой тип значения с набором именованных констант.An enum type is a distinct value type with a set of named constants. В следующем Color примере объявляется и используется тип перечисления с тремя константными значениями Green, Red, и Blue.The following example declares and uses an enum type named Color with three constant values, Red, Green, and Blue.

using System;

enum Color
{
    Red,
    Green,
    Blue
}

class Test
{
    static void PrintColor(Color color) {
        switch (color) {
            case Color.Red:
                Console.WriteLine("Red");
                break;
            case Color.Green:
                Console.WriteLine("Green");
                break;
            case Color.Blue:
                Console.WriteLine("Blue");
                break;
            default:
                Console.WriteLine("Unknown color");
                break;
        }
    }

    static void Main() {
        Color c = Color.Red;
        PrintColor(c);
        PrintColor(Color.Blue);
    }
}

Каждый тип перечисления имеет соответствующий целочисленный тип, называемый базовым типом перечисляемого типа.Each enum type has a corresponding integral type called the underlying type of the enum type. Тип перечисления, не объявляющий явно базовый тип, имеет базовый тип int.An enum type that does not explicitly declare an underlying type has an underlying type of int. Формат хранения типа Enum и диапазон возможных значений определяются базовым типом.An enum type's storage format and range of possible values are determined by its underlying type. Набор значений, которые может принимать тип enum, не ограничивается его элементами перечисления.The set of values that an enum type can take on is not limited by its enum members. В частности, любое значение базового типа перечисления может быть приведено к типу enum и является различно допустимым значением этого типа перечисления.In particular, any value of the underlying type of an enum can be cast to the enum type and is a distinct valid value of that enum type.

В следующем примере объявляется тип перечисления с именем Alignment и базовым sbyteтипом.The following example declares an enum type named Alignment with an underlying type of sbyte.

enum Alignment: sbyte
{
    Left = -1,
    Center = 0,
    Right = 1
}

Как показано в предыдущем примере, объявление члена перечисления может включать в себя константное выражение, указывающее значение элемента.As shown by the previous example, an enum member declaration can include a constant expression that specifies the value of the member. Постоянное значение для каждого члена перечисления должно находиться в диапазоне базового типа перечисления.The constant value for each enum member must be in the range of the underlying type of the enum. Если в объявлении члена перечисления явно не указано значение, элементу присваивается нулевое значение (если оно является первым элементом в типе enum) или значение текстового элемента, предшествующего перечислению, плюс один.When an enum member declaration does not explicitly specify a value, the member is given the value zero (if it is the first member in the enum type) or the value of the textually preceding enum member plus one.

Значения перечисления могут быть преобразованы в целочисленные значения и наоборот с помощью приведений типов.Enum values can be converted to integral values and vice versa using type casts. ПримерFor example

int i = (int)Color.Blue;        // int i = 2;
Color c = (Color)2;             // Color c = Color.Blue;

Значение по умолчанию любого типа перечисления — это целочисленное значение нуль, преобразованное в тип enum.The default value of any enum type is the integral value zero converted to the enum type. В случаях, когда переменные автоматически инициализируются значением по умолчанию, это значение присваивается переменным типов Enum.In cases where variables are automatically initialized to a default value, this is the value given to variables of enum types. Чтобы значение по умолчанию для типа Enum было легко доступно, литерал 0 неявно преобразуется в любой перечисляемый тип.In order for the default value of an enum type to be easily available, the literal 0 implicitly converts to any enum type. Таким образом, допустимо следующее выражение.Thus, the following is permitted.

Color c = 0;

ДелегатыDelegates

Тип delegate представляет ссылки на методы с конкретным списком параметров и типом возвращаемого значения.A delegate type represents references to methods with a particular parameter list and return type. Делегаты позволяют использовать методы как сущности, сохраняя их в переменные и передавая в качестве параметров.Delegates make it possible to treat methods as entities that can be assigned to variables and passed as parameters. Принцип работы делегатов близок к указателям функций из некоторых языков, но в отличие от указателей функций делегаты являются объектно-ориентированными и строго типизированными.Delegates are similar to the concept of function pointers found in some other languages, but unlike function pointers, delegates are object-oriented and type-safe.

Следующий пример кода объявляет и использует тип делегата с именем Function.The following example declares and uses a delegate type named Function.

using System;

delegate double Function(double x);

class Multiplier
{
    double factor;

    public Multiplier(double factor) {
        this.factor = factor;
    }

    public double Multiply(double x) {
        return x * factor;
    }
}

class Test
{
    static double Square(double x) {
        return x * x;
    }

    static double[] Apply(double[] a, Function f) {
        double[] result = new double[a.Length];
        for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
        return result;
    }

    static void Main() {
        double[] a = {0.0, 0.5, 1.0};
        double[] squares = Apply(a, Square);
        double[] sines = Apply(a, Math.Sin);
        Multiplier m = new Multiplier(2.0);
        double[] doubles =  Apply(a, m.Multiply);
    }
}

Экземпляр Function с типом делегата может ссылаться на любой метод, который принимает аргумент double и возвращает значение double.An instance of the Function delegate type can reference any method that takes a double argument and returns a double value. Метод применяет double[], Function заданный для элементов, и возвращает объект double[] с результатами. ApplyThe Apply method applies a given Function to the elements of a double[], returning a double[] with the results. В методе Main используется Apply для применения трех различных функций к double[].In the Main method, Apply is used to apply three different functions to a double[].

Делегат может ссылаться на статический метод (например, Square или Math.Sin в предыдущем примере) или метод экземпляра (например, m.Multiply в предыдущем примере).A delegate can reference either a static method (such as Square or Math.Sin in the previous example) or an instance method (such as m.Multiply in the previous example). Делегат, который ссылается на метод экземпляра, также содержит ссылку на конкретный объект. Когда метод экземпляра вызывается через делегат, этот объект превращается в this в вызове.A delegate that references an instance method also references a particular object, and when the instance method is invoked through the delegate, that object becomes this in the invocation.

Делегаты могут также создаваться с использованием анонимных функций, то есть создаваемых на ходу "встроенных методов".Delegates can also be created using anonymous functions, which are "inline methods" that are created on the fly. Анонимные функции могут использовать локальные переменные соседних методов.Anonymous functions can see the local variables of the surrounding methods. Таким образом, приведенный выше пример множителя можно легко написать без использования Multiplier класса:Thus, the multiplier example above can be written more easily without using a Multiplier class:

double[] doubles =  Apply(a, (double x) => x * 2.0);

Также стоит упомянуть о такой интересной и полезной особенности делегата, что он не имеет информации или ограничений в отношении того, к какому классу относится указанный в нем метод. Достаточно лишь, чтобы указанный метод имел такие же типы параметров и возвращаемого значения, которые назначены для делегата.An interesting and useful property of a delegate is that it does not know or care about the class of the method it references; all that matters is that the referenced method has the same parameters and return type as the delegate.

АтрибутыAttributes

Типы, члены и другие сущности в программе C# поддерживают модификаторы, которые управляют некоторыми аспектами их поведения.Types, members, and other entities in a C# program support modifiers that control certain aspects of their behavior. Например, доступность метода определяется с помощью модификаторов public, protected, internal и private.For example, the accessibility of a method is controlled using the public, protected, internal, and private modifiers. C# обобщает эту возможность, позволяя пользователям определять собственные типы декларативных сведений, назначать их для сущностей программы и извлекать во время выполнения.C# generalizes this capability such that user-defined types of declarative information can be attached to program entities and retrieved at run-time. В программах эти дополнительные декларативные сведения определяются и используются посредством атрибутов.Programs specify this additional declarative information by defining and using attributes.

Следующий пример кода объявляет атрибут HelpAttribute, который можно поместить в сущности программы для указания связей с соответствующей документацией.The following example declares a HelpAttribute attribute that can be placed on program entities to provide links to their associated documentation.

using System;

public class HelpAttribute: Attribute
{
    string url;
    string topic;

    public HelpAttribute(string url) {
        this.url = url;
    }

    public string Url {
        get { return url; }
    }

    public string Topic {
        get { return topic; }
        set { topic = value; }
    }
}

Все классы атрибутов являются производными от System.Attribute базового класса, предоставляемого .NET Framework.All attribute classes derive from the System.Attribute base class provided by the .NET Framework. Чтобы задать атрибут, его имя и возможные аргументы указываются в квадратных скобках непосредственно перед объявлением соответствующей сущности.Attributes can be applied by giving their name, along with any arguments, inside square brackets just before the associated declaration. Если имя атрибута оканчивается на Attribute, то его часть имени можно опустить при ссылке на этот атрибут.If an attribute's name ends in Attribute, that part of the name can be omitted when the attribute is referenced. Например, атрибут с именем HelpAttribute можно использовать так:For example, the HelpAttribute attribute can be used as follows.

[Help("http://msdn.microsoft.com/.../MyClass.htm")]
public class Widget
{
    [Help("http://msdn.microsoft.com/.../MyClass.htm", Topic = "Display")]
    public void Display(string text) {}
}

В HelpAttribute этом примере присоединяется Widget к Display классу, HelpAttribute а другой — к методу в классе.This example attaches a HelpAttribute to the Widget class and another HelpAttribute to the Display method in the class. Открытые конструкторы класса атрибута указывают, какие сведения необходимо указать при назначении атрибута некоторой сущности программы.The public constructors of an attribute class control the information that must be provided when the attribute is attached to a program entity. Дополнительные сведения можно предоставить через обращения к открытым свойствам класса атрибута, доступным для чтения и записи (например, как указанная выше ссылка на свойство Topic).Additional information can be provided by referencing public read-write properties of the attribute class (such as the reference to the Topic property previously).

В следующем примере показано, как можно получить сведения об атрибутах для данной сущности программы во время выполнения с помощью отражения.The following example shows how attribute information for a given program entity can be retrieved at run-time using reflection.

using System;
using System.Reflection;

class Test
{
    static void ShowHelp(MemberInfo member) {
        HelpAttribute a = Attribute.GetCustomAttribute(member,
            typeof(HelpAttribute)) as HelpAttribute;
        if (a == null) {
            Console.WriteLine("No help for {0}", member);
        }
        else {
            Console.WriteLine("Help for {0}:", member);
            Console.WriteLine("  Url={0}, Topic={1}", a.Url, a.Topic);
        }
    }

    static void Main() {
        ShowHelp(typeof(Widget));
        ShowHelp(typeof(Widget).GetMethod("Display"));
    }
}

Когда выполняется запрос конкретного атрибута через отражение, вызывается конструктор для класса атрибута с указанием сведений, представленных в исходном коде программы, а затем возвращается созданный экземпляр атрибута.When a particular attribute is requested through reflection, the constructor for the attribute class is invoked with the information provided in the program source, and the resulting attribute instance is returned. Если дополнительные сведения предоставляются через свойства, перед возвращением экземпляра атрибута этим свойствам присваиваются указанные значения.If additional information was provided through properties, those properties are set to the given values before the attribute instance is returned.