Встроенные ссылочные типы (справочник по C#)
C# имеет множество встроенных ссылочных типов. У них есть ключевые слова или операторы, которые являются синонимами типа в библиотеке .NET.
Тип object
Тип object является псевдонимом System.Object в .NET. В унифицированной системе типов C# все типы, стандартные и определяемые пользователем, ссылочные типы и типы значений напрямую или косвенно наследуются из System.Object. Переменным типа object можно назначать значения любого типа. Любой переменной object можно назначить значение по умолчанию с помощью литерала null . При преобразовании переменной типа значения в объект она считается упакованой. Когда переменная типа object преобразуется в тип значения, она считается распаковкой. Дополнительные сведения см. в разделе Упаковка-преобразование и распаковка-преобразование.
Тип string
Тип string представляет последовательность, состоящую из нуля или более символов в кодировке Юникод. string является псевдонимом для System.String в .NET.
Несмотря на то что string представляет собой ссылочный тип, операторы равенства == и != по определению сравнивают не ссылки, а значения объектов string . Равенство на основе значений делает тестирование на равенство строк более интуитивно понятным. Пример:
string a = "hello"; string b = "h"; // Append to contents of 'b' b += "ello"; Console.WriteLine(a == b); Console.WriteLine(object.ReferenceEquals(a, b));
В предыдущем примере отображается значение True, а затем False, так как содержимое строк эквивалентно, но a не ссылается на один и b тот же экземпляр строки.
Приведенный выше код создает строковый объект, содержащий «доброе утро».
Строки являются неизменяемыми. Содержимое строкового объекта невозможно изменить после создания объекта. Например, при написании кода компилятор фактически создает новый строковый объект для хранения новой последовательности символов, а затем этот новый объект назначается b . Память, выделенная для b (если он содержит строку h), затем доступна для сборки мусора.
Оператор [] можно использовать для доступа только для чтения к отдельным символам строки. Допустимые значения индекса начинаются с 0 и должны быть меньше, чем длина строки:
string str = "test"; char x = str[2]; // x = 's';
Также оператор [] можно использовать для итерации каждого символа в строке:
string str = "test"; for (int i = 0; i < str.Length; i++) < Console.Write(str[i] + " "); >// Output: t e s t
Строковые литералы
Строковые литералы имеют тип string и могут быть записаны в трех формах: необработанные, кавычки и дословно.
Необработанные строковые литералы доступны начиная с C# 11. Необработанные строковые литералы могут содержать произвольный текст без использования escape-последовательностей. Необработанные строковые литералы могут содержать пробелы и новые строки, внедренные кавычки и другие специальные символы. Необработанные строковые литералы заключаются как минимум в три двойные кавычки («»» ):
""" This is a multi-line string literal with the second line indented. """
Можно даже включить последовательность из трех (или более) символов в двойных кавычках. Если для текста требуется внедренная последовательность кавычек, при необходимости вы начинаете и заканчиваете необработанный строковый литерал с дополнительными кавычками:
""""" This raw string literal has four """", count them: """" four! embedded quote characters in a sequence. That's why it starts and ends with five double quotes. You could extend this example with as many embedded quotes as needed for your text. """""
Необработанные строковые литералы обычно имеют начальную и конечную последовательности кавычек в отдельных строках внедренного текста. Многостроковые необработанные строковые литералы поддерживают строки, которые сами являются строками в кавычках:
var message = """ "This is a very important message." """; Console.WriteLine(message); // output: "This is a very important message."
Если начальные и конечные кавычки находятся в отдельных строках, новые строки после открывающей кавычки и перед конечной кавычками не включаются в итоговое содержимое. Закрывающая последовательность кавычек определяет крайний левый столбец для строкового литерала. Необработанный строковый литерал можно навести отступ в соответствии с общим форматом кода:
var message = """ "This is a very important message." """; Console.WriteLine(message); // output: "This is a very important message." // The leftmost whitespace is not part of the raw string literal
Столбцы справа от конечной последовательности кавычек сохраняются. Это позволяет использовать необработанные строки для таких форматов данных, как JSON, YAML или XML, как показано в следующем примере:
Компилятор выдает ошибку, если какая-либо из текстовых строк простирается слева от закрывающей последовательности кавычек. Открывающая и закрывающая последовательности кавычек могут находиться в одной строке, при этом строковый литерал не начинается и не заканчивается символом кавычки:
var shortText = """He said "hello!" this morning.""";
Необработанные строковые литералы можно объединить с интерполяцией строк , чтобы включить в выходную строку символы кавычек и фигурные скобки.
Строковые литералы в кавычках заключаются в двойные кавычки («):
"good morning" // a string literal
Строковые литералы могут содержать любые символьные литералы. Escape-последовательности включены. В следующем примере escape-последовательность \\ используется для получения обратной косой черты, \u0066 — для получения буквы f, и \n — для получения новой строки.
string a = "\\\u0066\n F"; Console.WriteLine(a); // Output: // \f // F
Escape-код \udddd (где dddd состоит из четырех цифр) представляет символ Юникода U+ dddd . Также распознаются восьмизначные escape-коды Юникода: \Udddddddd .
Буквальные строковые литералы начинаются с @ и также заключаются в двойные кавычки. Пример:
@"good morning" // a string literal
Преимущество буквальных строк заключается в том, что escape-последовательности не обрабатываются , что упрощает запись. Например, следующий текст соответствует полному имени файла Windows:
@"c:\Docs\Source\a.txt" // rather than "c:\\Docs\\Source\\a.txt"
Чтобы включить двойную кавычку в строку с @кавычками, удвойте ее:
@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
Строковые литералы UTF-8
Строки в .NET хранятся в кодировке UTF-16. UTF-8 — это стандарт для веб-протоколов и других важных библиотек. Начиная с C# 11, можно добавить суффикс u8 в строковый литерал, чтобы указать кодировку UTF-8. Литералы UTF-8 хранятся в виде ReadOnlySpan объектов. Естественный тип строкового литерала UTF-8 — ReadOnlySpan . Использование строкового литерала UTF-8 создает более четкое объявление, чем объявление эквивалентного System.ReadOnlySpan , как показано в следующем коде:
ReadOnlySpan AuthWithTrailingSpace = new byte[] < 0x41, 0x55, 0x54, 0x48, 0x20 >; ReadOnlySpan AuthStringLiteral = "AUTH "u8;
Чтобы сохранить строковый литерал UTF-8 в виде массива, необходимо использовать для копирования ReadOnlySpan.ToArray() байтов, содержащих литерал, в изменяемый массив:
byte[] AuthStringLiteral = "AUTH "u8.ToArray();
Строковые литералы UTF-8 не являются константами времени компиляции; они — константы среды выполнения. Поэтому их нельзя использовать в качестве значения по умолчанию для необязательного параметра. Строковые литералы UTF-8 нельзя сочетать с интерполяцией строк. Нельзя использовать маркер и суффикс $ u8 в одном строковом выражении.
Тип delegate
Объявление типа делегата аналогично сигнатуре метода. Оно имеет возвращаемое значение и любое число параметров любого типа:
public delegate void MessageDelegate(string message); public delegate int AnotherDelegate(MyType m, long num);
В .NET типы System.Action и System.Func предоставляют общие определения для многих распространенных делегатов. Скорее всего, не нужно определять новые пользовательские типы делегата. Вместо этого можно создать экземпляры предоставленных универсальных типов.
Ключевое слово delegate имеет ссылочный тип, который можно использовать для инкапсуляции именованного или анонимного метода. Делегаты аналогичны используемым в языке C++ указателям функций, но являются типобезопасными и безопасными. Сведения о применении делегатов см. в разделах Делегаты и Универсальные делегаты. Делегаты являются основой событий. Экземпляры делегата могут создаваться путем его связывания с именованным или анонимным методом.
Делегат должен быть создан при помощи метода или лямбда-выражения, имеющего совместимые возвращаемый тип и входные параметры. Дополнительные сведения о допустимой степени вариации сигнатур методов см. в разделе Вариативность в делегатах. Для использования с анонимными методами делегат и код, который должен быть связан с ним, должны быть объявлены вместе.
Сочетание или удаление делегатов завершается сбоем с исключением среды выполнения, если типы делегатов, задействованные во время выполнения, отличаются из-за преобразования вариантов. В следующем примере показана ситуация, которая завершается сбоем:
Action stringAction = str => <>; Action objectAction = obj => <>; // Valid due to implicit reference conversion of // objectAction to Action, but may fail // at run time. Action combination = stringAction + objectAction;
Вы можете создать делегат с правильным типом среды выполнения, создав новый объект делегата. В следующем примере показано, как это решение можно применить к предыдущему примеру.
Action stringAction = str => <>; Action objectAction = obj => <>; // Creates a new delegate instance with a runtime type of Action. Action wrappedObjectAction = new Action(objectAction); // The two Action delegate instances can now be combined. Action combination = stringAction + wrappedObjectAction;
Начиная с C# 9, можно объявить указатели функций, которые используют аналогичный синтаксис. Указатель функции использует инструкцию calli вместо создания экземпляра типа делегата и вызова виртуального метода Invoke .
Тип dynamic
Тип dynamic указывает, что использование переменной и ссылок на ее члены обходит проверку типа во время компиляции. Такие операции разрешаются во время выполнения. Тип dynamic упрощает доступ к API COM, таким как API автоматизации Office, к динамическим API, таким как библиотеки IronPython, и к HTML-модели DOM.
Тип dynamic в большинстве случаев ведет себя как тип object . В частности, можно преобразовать любое выражение, отличное от NULL, в тип dynamic . Тип dynamic отличается от object тем, что операции, содержащие выражения типа dynamic , не разрешаются или не проверяются компилятором. Компилятор объединяет сведения об операции, которые впоследствии будут использоваться для оценки этой операции во время выполнения. В рамках этого процесса переменные типа dynamic компилируются в переменные типа object . Таким образом, тип dynamic существует только во время компиляции, но не во время выполнения.
В следующем примере переменной типа dynamic противопоставляется переменная типа object . Чтобы проверить тип каждой переменной во время компиляции, наведите указатель мыши на dyn или obj в операторах WriteLine . Скопируйте следующий код в редактор, где доступен IntelliSense. IntelliSense отображает dynamic для dyn и object для obj .
Операторы WriteLine отображают типы времени выполнения dyn и obj . На этом этапе оба имеют один и тот же тип — целое число. Выводятся следующие результаты.
Чтобы увидеть разницу между dyn и obj во время компиляции, добавьте между объявлениями и операторами WriteLine в предыдущем примере следующие две строки:
При попытке добавления целого числа и объекта в выражение obj + 3 выдается ошибка компилятора. При этом для dyn + 3 ошибка не возникает. Выражение, содержащее dyn , не проверяется во время компиляции dyn , так как типом является dynamic .
В следующем примере dynamic используется в нескольких объявлениях. Метод Main также противопоставляет проверку типов во время компиляции.
using System; namespace DynamicExamples < class Program < static void Main(string[] args) < ExampleClass ec = new ExampleClass(); Console.WriteLine(ec.ExampleMethod(10)); Console.WriteLine(ec.ExampleMethod("value")); // The following line causes a compiler error because ExampleMethod // takes only one argument. //Console.WriteLine(ec.ExampleMethod(10, 4)); dynamic dynamic_ec = new ExampleClass(); Console.WriteLine(dynamic_ec.ExampleMethod(10)); // Because dynamic_ec is dynamic, the following call to ExampleMethod // with two arguments does not produce an error at compile time. // However, it does cause a run-time error. //Console.WriteLine(dynamic_ec.ExampleMethod(10, 4)); >> class ExampleClass < static dynamic _field; dynamic Prop < get; set; >public dynamic ExampleMethod(dynamic d) < dynamic local = "Local variable"; int two = 2; if (d is int) < return local; >else < return two; >> > > // Results: // Local variable // 2 // Local variable
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:
См. также раздел
- Справочник по C#
- Ключевые слова в C#
- События
- Использование типа dynamic
- Рекомендации по использованию строк
- Базовые операции со строками в .NET Framework
- Создание строк
- Операторы приведения и тестирования типов
- Практическое руководство. Безопасное приведение с помощью сопоставления шаблонов, а также операторов is и as
- Пошаговое руководство. Создание и использование динамических объектов (C# и Visual Basic)
- System.Object
- System.String
- System.Dynamic.DynamicObject