Типы (Руководство по программированию на C#)Types (C# Programming Guide)

Типы, переменные и значенияTypes, variables, and values

C# является строго типизированным языком.C# is a strongly-typed language. Каждая переменная и константа имеет тип, как и каждое выражение, результатом вычисления которого является значение.Every variable and constant has a type, as does every expression that evaluates to a value. Сигнатура каждого метода задает тип для каждого входного параметра и для возвращаемого значения.Every method signature specifies a type for each input parameter and for the return value. Библиотека классов .NET определяет набор встроенных числовых типов, а также более сложных типов, представляющих широкое разнообразие логических конструкций, например файловую систему, сетевые подключения, коллекции, массивы объектов и даты.The .NET class library defines a set of built-in numeric types as well as more complex types that represent a wide variety of logical constructs, such as the file system, network connections, collections and arrays of objects, and dates. Обычная программа на C# использует типы из этой библиотеки классов и пользовательские типы, которые моделируют уникальные концепции конкретной сферы применения.A typical C# program uses types from the class library as well as user-defined types that model the concepts that are specific to the program's problem domain.

В типах может храниться следующая информация:The information stored in a type can include the following:

  • место, необходимое для хранения переменной этого типа;The storage space that a variable of the type requires.

  • максимальное и минимальное значения, которые могут быть представлены;The maximum and minimum values that it can represent.

  • содержащиеся в типе члены (методы, поля, события и т. д.);The members (methods, fields, events, and so on) that it contains.

  • базовый тип, от которого наследует этот тип;The base type it inherits from.

  • расположение, в котором будет выделена память для переменных во время выполнения;The location where the memory for variables will be allocated at run time.

  • разрешенные виды операций.The kinds of operations that are permitted.

Компилятор использует сведения о типах, чтобы проверить, все ли операции, выполняемые в коде, являются типобезопасными.The compiler uses type information to make sure that all operations that are performed in your code are type safe. Например, при объявлении переменной типа int компилятор позволяет в дополнение использовать переменную и операции вычитания.For example, if you declare a variable of type int, the compiler allows you to use the variable in addition and subtraction operations. При попытке выполнить эти же операции для переменной типа bool компилятор выдаст ошибку, как показано в следующем примере:If you try to perform those same operations on a variable of type bool, the compiler generates an error, as shown in the following example:

int a = 5;             
int b = a + 2; //OK

bool test = true;
  
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;

Примечание

Тем, кто ранее использовал C и C++, нужно обратить внимание на то, что в C# тип bool нельзя преобразовать в int.C and C++ developers, notice that in C#, bool is not convertible to int.

Компилятор внедряет сведения о типе в исполняемый файл в виде метаданных.The compiler embeds the type information into the executable file as metadata. Среда CLR использует эти метаданные во время выполнения для дальнейшего обеспечения безопасности типа при выделении и освобождении памяти.The common language runtime (CLR) uses that metadata at run time to further guarantee type safety when it allocates and reclaims memory.

Задание типов в объявлениях переменныхSpecifying types in variable declarations

При объявлении переменной или константы в программе необходимо либо задать ее тип, либо использовать ключевое слово var, чтобы дать возможность компилятору определить его.When you declare a variable or constant in a program, you must either specify its type or use the var keyword to let the compiler infer the type. В следующем примере показаны некоторые объявления переменных, использующие встроенные числовые типы и сложные пользовательские типы:The following example shows some variable declarations that use both built-in numeric types and complex user-defined types:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
            where item <= limit
            select item;

Типы для параметров и возвращаемых значений метода задаются в сигнатуре метода.The types of method parameters and return values are specified in the method signature. Далее представлена сигнатура метода, который требует значение int в качестве входного аргумента и возвращает строку:The following signature shows a method that requires an int as an input argument and returns a string:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = { "Spencer", "Sally", "Doug" };

После объявления переменной она не может быть повторно объявлена с новым типом, и ей нельзя присвоить значение, несовместимое с ее объявленным типом.After a variable is declared, it cannot be re-declared with a new type, and it cannot be assigned a value that is not compatible with its declared type. Например, нельзя объявить переменную типа int и затем присвоить ей логическое значение true.For example, you cannot declare an int and then assign it a Boolean value of true. Однако значения могут быть преобразованы в другие типы, например при сохранении в другие переменные или при передаче в качестве аргументов метода.However, values can be converted to other types, for example when they are assigned to new variables or passed as method arguments. Если преобразование типов не приводит к потере данных, она выполняется компилятором автоматически.A type conversion that does not cause data loss is performed automatically by the compiler. Для преобразования, которое может привести к потере данных, необходимо выполнить приведение в исходном коде.A conversion that might cause data loss requires a cast in the source code.

Дополнительные сведения см. в разделе Приведение и преобразование типов.For more information, see Casting and Type Conversions.

Встроенные типыBuilt-in types

C# предоставляет стандартный набор встроенных числовых типов для представления целых чисел, значений с плавающей запятой, логических выражений, текстовых символов, десятичных значений и других типов данных.C# provides a standard set of built-in numeric types to represent integers, floating point values, Boolean expressions, text characters, decimal values, and other types of data. Также существуют встроенные типы string и object.There are also built-in string and object types. Они доступны для использования в любой программе C#.These are available for you to use in any C# program. Дополнительные сведения о встроенных типах см. в Справочных таблицах по встроенным типам.For more information about the built-in types, see Reference Tables for built-in Types.

Пользовательские типыCustom types

Вы можете создавать собственные типы, используя конструкции структура, класс, интерфейс и перечисление.You use the struct, class, interface, and enum constructs to create your own custom types. Сама библиотека классов .NET — это коллекция пользовательских типов, предоставленных корпорацией Майкрософт, которые вы можете свободно использовать в приложениях.The .NET class library itself is a collection of custom types provided by Microsoft that you can use in your own applications. По умолчанию в любой программе C# доступны наиболее часто используемые типы из библиотеки классов.By default, the most frequently used types in the class library are available in any C# program. Чтобы сделать доступными другие типы, нужно явным образом добавить в проект ссылку на сборку, в которой они определены.Others become available only when you explicitly add a project reference to the assembly in which they are defined. Если компилятору предоставлена ссылка на сборку, то вы можете объявлять в коде переменные (и константы) любых типов, объявленных в этой сборке.After the compiler has a reference to the assembly, you can declare variables (and constants) of the types declared in that assembly in source code. См. дополнительные сведения о библиотеке классов .NET.For more information, see .NET Class Library.

Система общих типов CTSThe common type system

Важно понимать два фундаментальных тезиса о системе типов, используемой в .NET:It is important to understand two fundamental points about the type system in .NET:

  • Она поддерживает принцип наследования.It supports the principle of inheritance. Типы могут быть производными от других типов, которые называются базовыми типами.Types can derive from other types, called base types. Производный тип наследует все (с некоторыми ограничениями) методы, свойства и другие члены базового типа.The derived type inherits (with some restrictions) the methods, properties, and other members of the base type. Базовый тип, в свою очередь, может быть производным от какого-то другого типа, при этом производный тип наследует члены обоих базовых типов в иерархии наследования.The base type can in turn derive from some other type, in which case the derived type inherits the members of both base types in its inheritance hierarchy. Все типы, включая встроенные числовые типы, например System.Int32 (ключевое слово C#: int), в конечном счете являются производными от одного базового типа System.Object (ключевое слово C#: object).All types, including built-in numeric types such as System.Int32 (C# keyword: int), derive ultimately from a single base type, which is System.Object (C# keyword: object). Эта унифицированная иерархия типов называется Системой общих типов CTS.This unified type hierarchy is called the Common Type System (CTS). Дополнительные сведения о наследовании в C# см. в статье Inheritance (Наследование).For more information about inheritance in C#, see Inheritance.

  • Каждый тип в CTS определяется как тип значения либо ссылочный тип.Each type in the CTS is defined as either a value type or a reference type. Это справедливо и для всех пользовательских типов, в том числе включенных в библиотеку классов .NET или определенных вами.This includes all custom types in the .NET class library and also your own user-defined types. Если в определении типа используется ключевое слово struct, он является типом значения. Например, все встроенные числовые типы определены как structs.Types that you define by using the struct keyword are value types; all the built-in numeric types are structs. Если в определении типа используется ключевое слово class, он является ссылочным типом.Types that you define by using the class keyword are reference types. Для ссылочных типов и типов значений используются разные правила компиляции, и они демонстрируют разное поведение во время выполнения.Reference types and value types have different compile-time rules, and different run-time behavior.

Ниже показаны взаимоотношения между типами значения и ссылочными типами в CTS.The following illustration shows the relationship between value types and reference types in the CTS.

На следующем изображении показаны типы значений и ссылочные типы в CTS:The following image shows value types and reference types in the CTS:

Снимок экрана, где показаны типы значений и ссылочные типы в CTS.

Примечание

Как видно, все наиболее часто используемые типы организованы в пространство имен System.You can see that the most commonly used types are all organized in the System namespace. Но само по себе пространство имен, в котором размещен тип, никак не зависит от того, является ли он типом значения или ссылочным типом.However, the namespace in which a type is contained has no relation to whether it is a value type or reference type.

Типы значенийValue types

Типы значений являются производными от System.ValueType, который является производным от System.Object.Value types derive from System.ValueType, which derives from System.Object. Типы, производные от System.ValueType, имеют особое поведение в среде CLR.Types that derive from System.ValueType have special behavior in the CLR. Переменные типа значения напрямую содержат свои значения, что означает, что память выделяется в любом контексте объявления переменной.Value type variables directly contain their values, which means that the memory is allocated inline in whatever context the variable is declared. Нет раздельного размещения в куче или накладных расходов при сборке мусора для переменных типа значения.There is no separate heap allocation or garbage collection overhead for value-type variables.

Существует две категории типов значений: структура и перечисление.There are two categories of value types: struct and enum.

Встроенные числовые типы являются структурами и имеют свойства и методы, к которым можно обращаться.The built-in numeric types are structs, and they have properties and methods that you can access:

// Static method on type byte.
byte b = byte.MaxValue;

Но объявление и присвоение значений вы выполняете для них так, как если бы они были простыми нестатистическими типами.But you declare and assign values to them as if they were simple non-aggregate types:

byte num = 0xA;
int i = 5;
char c = 'Z';

Типы значений являются запечатанными, что означает, например, что нельзя наследовать тип от System.Int32 и нельзя определить структуру для наследования от любого пользовательского класса или структуры, поскольку структура может наследовать только от System.ValueType.Value types are sealed, which means, for example, that you cannot derive a type from System.Int32, and you cannot define a struct to inherit from any user-defined class or struct because a struct can only inherit from System.ValueType. Тем не менее структура может реализовывать один или несколько интерфейсов.However, a struct can implement one or more interfaces. Вы можете привести тип структуры к любому типу интерфейса, который она реализует. Это приведет к операции упаковки-преобразования, которая упакует структуру внутри объекта ссылочного типа в управляемой куче.You can cast a struct type to any interface type that it implements; this causes a boxing operation to wrap the struct inside a reference type object on the managed heap. Операции упаковки-преобразования выполняются при передаче типа значения в метод, принимающий System.Object или любой тип интерфейса в качестве входного параметра.Boxing operations occur when you pass a value type to a method that takes a System.Object or any interface type as an input parameter. Дополнительные сведения см. в разделе Упаковка-преобразование и распаковка-преобразование.For more information, see Boxing and Unboxing.

Используйте ключевое слово struct, чтобы создать собственные пользовательские типы значений.You use the struct keyword to create your own custom value types. Как правило, структура используется как контейнер для небольшого набора связанных переменных, как показано в следующем примере:Typically, a struct is used as a container for a small set of related variables, as shown in the following example:

public struct Coords
{
    public int x, y;

    public Coords(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

Дополнительные сведения о структурах см. в статье Structs (Структуры).For more information about structs, see Structs. См. дополнительные сведения о типах значений.For more information about value types in .NET, see Value Types.

Еще одна категория типов значений — это перечисления.The other category of value types is enum. Перечисление определяет набор именованных целочисленных констант.An enum defines a set of named integral constants. Например, перечисление System.IO.FileMode из библиотеки классов .NET содержит набор именованных целочисленных констант, которые определяют правила открытия файла.For example, the System.IO.FileMode enumeration in the .NET class library contains a set of named constant integers that specify how a file should be opened. В следующем примере представлено определение этого типа:It is defined as shown in the following example:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Константа System.IO.FileMode.Create имеет значение 2.The System.IO.FileMode.Create constant has a value of 2. Поскольку имена намного лучше воспринимаются человеком при изучении исходного кода, мы рекомендуем всегда использовать перечисления вместо литеральных числовых констант.However, the name is much more meaningful for humans reading the source code, and for that reason it is better to use enumerations instead of constant literal numbers. Дополнительные сведения можно найти по адресу: System.IO.FileMode.For more information, see System.IO.FileMode.

Все перечисления наследуют от System.Enum, который наследует от System.ValueType.All enums inherit from System.Enum, which inherits from System.ValueType. К перечислениям применимы все те же правила, что к структурам.All the rules that apply to structs also apply to enums. Дополнительные сведения см. в статье Enumeration Types (Типы перечислений).For more information about enums, see Enumeration Types.

Ссылочные типыReference types

Тип, который определен как класс, делегат, массив или интерфейс, называется ссылочным типом.A type that is defined as a class, delegate, array, or interface is a reference type. Когда во время выполнения вы объявляете переменную ссылочного типа, такая переменная будет содержать значение NULL, пока вы явным образом не создадите объект с помощью оператора new или не назначите его объекту, созданному в другом месте, используя new, как показано в следующем примере:At run time, when you declare a variable of a reference type, the variable contains the value null until you explicitly create an object by using the new operator, or assign it an object that has been created elsewhere by using new, as shown in the following example:

MyClass mc = new MyClass();
MyClass mc2 = mc;

Интерфейс должен быть инициализирован вместе с объектом класса, который его реализует.An interface must be initialized together with a class object that implements it. Если MyClass реализует IMyInterface, экземпляр IMyInterface создается так, как показано в следующем примере:If MyClass implements IMyInterface, you create an instance of IMyInterface as shown in the following example:

IMyInterface iface = new MyClass();

При создании объекта выделяется память в управляемой куче, и переменная хранит только ссылку на расположение объекта.When the object is created, the memory is allocated on the managed heap, and the variable holds only a reference to the location of the object. Хранение типов в управляемой куче требует дополнительных действий как при выделении памяти, так и при удалении, которое выполняется функцией автоматического управления памятью в среде CLR, известной как сборка мусора.Types on the managed heap require overhead both when they are allocated and when they are reclaimed by the automatic memory management functionality of the CLR, which is known as garbage collection. Сборка мусора является высоко оптимизированным процессом и в большинстве случаев она не создает помех для производительности.However, garbage collection is also highly optimized, and in most scenarios it does not create a performance issue. Дополнительные сведения о сборке мусора см. в статье Автоматическое управление памятью.For more information about garbage collection, see Automatic Memory Management.

Все массивы являются ссылочными типами, даже если их элементы являются типами значений.All arrays are reference types, even if their elements are value types. Массивы являются неявно производными от класса System.Array, но в C# их можно объявлять и использовать с упрощенным синтаксисом, как показано в следующем примере:Arrays implicitly derive from the System.Array class, but you declare and use them with the simplified syntax that is provided by C#, as shown in the following example:

// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };

// Access an instance property of System.Array.
int len = nums.Length;

Ссылочные типы полностью поддерживают наследование.Reference types fully support inheritance. Создаваемый класс может наследовать от любого другого интерфейса или класса, который не определен как запечатанный, а другие классы могут наследовать от этого класса и переопределять его виртуальные методы.When you create a class, you can inherit from any other interface or class that is not defined as sealed, and other classes can inherit from your class and override your virtual methods. Дополнительные сведения о создании собственных классов см. в статье Classes and Structs (Классы и структуры).For more information about how to create your own classes, see Classes and Structs. Дополнительные сведения о наследовании в C# и виртуальных методах см. в статье Inheritance (Наследование).For more information about inheritance and virtual methods, see Inheritance.

Типы литеральных значенийTypes of literal values

В C# литеральные значения получают тип от компилятора.In C#, literal values receive a type from the compiler. Вы можете указать способ типизации числового литерала, добавив букву в конце числа.You can specify how a numeric literal should be typed by appending a letter to the end of the number. Например, чтобы значение 4,56 рассматривалось как значение с плавающей запятой, добавьте после этого числа букву "f" или "F": 4.56f.For example, to specify that the value 4.56 should be treated as a float, append an "f" or "F" after the number: 4.56f. Если буква отсутствует, компилятор самостоятельно выберет тип для литерала.If no letter is appended, the compiler will infer a type for the literal. Дополнительные сведения о том, какие типы могут быть указаны с буквенными суффиксами, см. на страницах справки по отдельным типам в разделе Типы значений.For more information about which types can be specified with letter suffixes, see the reference pages for individual types in Value Types.

Поскольку литералы являются типизированными и все типы в конечном счете являются производными от System.Object, можно написать и скомпилировать следующий код:Because literals are typed, and all types derive ultimately from System.Object, you can write and compile code such as the following:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

универсальные типы;Generic types

Тип может быть объявлен с одним или несколькими параметрами типа, служащими в качестве местозаполнителя для фактического типа (конкретного типа), который клиентский код предоставит при создании экземпляра типа.A type can be declared with one or more type parameters that serve as a placeholder for the actual type (the concrete type) that client code will provide when it creates an instance of the type. Такие типы называются универсальными типами.Such types are called generic types. Например, тип .NET System.Collections.Generic.List<T> имеет один параметр типа, которому в соответствии с соглашением присвоено имя T. При создании экземпляра этого типа необходимо указать тип объектов, которые будут содержаться в списке. Например, так используется список строковых значений:For example, the .NET type System.Collections.Generic.List<T> has one type parameter that by convention is given the name T. When you create an instance of the type, you specify the type of the objects that the list will contain, for example, string:

List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);

Использование параметра типа позволяет повторно использовать один же класс для хранения элементов любого типа, не преобразовывая каждый элемент в объект.The use of the type parameter makes it possible to reuse the same class to hold any type of element, without having to convert each element to object. Универсальные классы коллекций называются строго типизированными коллекциями, так как компилятору известен конкретный тип элементов коллекции и он может выдать ошибку во время компиляции, если, к примеру, вы попытаетесь добавить целое число в объект stringList, созданный в предыдущем примере.Generic collection classes are called strongly-typed collections because the compiler knows the specific type of the collection's elements and can raise an error at compile-time if, for example, you try to add an integer to the stringList object in the previous example. Дополнительные сведения см. в статье Универсальные шаблоны.For more information, see Generics.

Неявные типы, анонимные типы и типы, допускающие значение NULLImplicit types, anonymous types, and nullable value types

Как уже говорилось ранее, можно неявно типизировать локальную переменную (но не члены класса) с помощью ключевого слова var.As stated previously, you can implicitly type a local variable (but not class members) by using the var keyword. Такая переменная получает конкретный тип во время компиляции, но этот тип предоставляется компилятором.The variable still receives a type at compile time, but the type is provided by the compiler. Дополнительные сведения см. в статье Implicitly Typed Local Variables (Неявно типизированные локальные переменные).For more information, see Implicitly Typed Local Variables.

В некоторых случаях нет смысла создавать именованный тип для простых наборов связанных значений, которые не будут сохранены или переданы за пределы метода.In some cases, it is inconvenient to create a named type for simple sets of related values that you do not intend to store or pass outside method boundaries. В таких случаях можно применить анонимные типы.You can create anonymous types for this purpose. Дополнительные сведения см. в статье Анонимные типы.For more information, see Anonymous Types.

Обычные типы значений не могут иметь значение NULL.Ordinary value types cannot have a value of null. Но вы можете создать специальные типы, допускающие значения NULL, добавив символ ? после имени типа.However, you can create nullable value types by affixing a ? after the type. Например, тип int? является типом int, который может иметь значение NULL.For example, int? is an int type that can also have the value null. Типы, допускающие значение NULL, представляют собой экземпляры универсального типа структуры System.Nullable<T>.Nullable value types are instances of the generic struct type System.Nullable<T>. Типы, допускающие значение NULL, особенно полезны при передаче данных в базы данных, где числовые значения могут иметь значение NULL, и из них.Nullable value types are especially useful when you are passing data to and from databases in which numeric values might be null. Дополнительные сведения см. в разделе Типы, допускающие значение NULL.For more information, see Nullable value types.

Дополнительные сведения см. в следующих разделах:For more information, see the following topics:

Спецификация языка C#C# language specification

Дополнительные сведения см. в спецификации языка C#.For more information, see the C# Language Specification. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.The language specification is the definitive source for C# syntax and usage.

См. такжеSee also