оператор using — обеспечение правильного использования удаленных объектов
Инструкция using гарантирует правильное использование экземпляра IDisposable :
var numbers = new List(); using (StreamReader reader = File.OpenText("numbers.txt")) < string line; while ((line = reader.ReadLine()) is not null) < if (int.TryParse(line, out int number)) < numbers.Add(number); >> >
Когда элемент управления покидает блок инструкции using , полученный IDisposable экземпляр удаляется. В частности, инструкция гарантирует, что удаленный экземпляр удаляется даже в том случае, using если исключение возникает в блоке using инструкции. В предыдущем примере открытый файл закрывается после обработки всех строк.
Используйте инструкцию await using для правильного использования экземпляра IAsyncDisposable :
await using (var resource = new AsyncDisposableExample()) < // Use the resource >
Дополнительные сведения об использовании экземпляров см. в разделе «Использование асинхронногоIAsyncDisposable удаления» статьи «Реализация метода DisposeAsync».
Вы также можете использовать using объявление , которое не требует фигурных скобок:
static IEnumerable LoadNumbers(string filePath) < using StreamReader reader = File.OpenText(filePath); var numbers = new List(); string line; while ((line = reader.ReadLine()) is not null) < if (int.TryParse(line, out int number)) < numbers.Add(number); >> return numbers; >
При объявлении using локальная переменная удаляется в конце область, в которой она объявлена. В предыдущем примере удаление происходит в конце метода.
Переменная, объявленная оператором или объявлением using , является чтением. Его нельзя переназначить или передать в качестве ref параметра out .
Можно объявить несколько экземпляров одного типа в одной using инструкции, как показано в следующем примере:
using (StreamReader numbersFile = File.OpenText("numbers.txt"), wordsFile = File.OpenText("words.txt")) < // Process both files >
При объявлении нескольких экземпляров в одном using операторе они удаляются в обратном порядке объявления.
Вы также можете использовать инструкцию using и объявление с экземпляром структуры ссылок, которая соответствует шаблону удаления. То есть он имеет метод экземпляра Dispose , который доступен, без параметров и имеет тип возвращаемого void значения.
Оператор using также может иметь следующую форму:
using (expression) < // . >
где expression создается удаленный экземпляр. Это показано в следующем примере:
StreamReader reader = File.OpenText(filePath); using (reader) < // Process file content >
В предыдущем примере после того, как элемент управления покидает инструкцию using , удаленный экземпляр остается в область пока он уже удален. Если этот экземпляр используется дальше, может возникнуть исключение, например ObjectDisposedException. Поэтому рекомендуется объявлять удаленную переменную в using инструкции или с объявлением using .
Спецификация языка C#
См. также
- справочник по C#
- System.IDisposable
- System.IAsyncDisposable
- Использование объектов, реализующих IDisposable
- Реализация метода Dispose
- Реализация метода DisposeAsync
- Использование простого оператора using (правило стиля IDE0063)
- using Директива
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Пространства имен (C++)
Пространство имен — это декларативная область, в рамках которой определяются различные идентификаторы (имена типов, функций, переменных, и т. д.). Пространства имен используются для организации кода в виде логических групп и с целью избежания конфликтов имен, которые могут возникнуть, особенно в таких случаях, когда база кода включает несколько библиотек. Все идентификаторы в пределах пространства имен доступны друг другу без уточнения. Идентификаторы за пределами пространства имен могут получить доступ к членам с помощью полного имени для каждого идентификатора, например, с помощью объявления для одного идентификатора ( using std::string ) или директивы using для всех идентификаторов в пространстве имен ( using namespace std; ). std::vector vec; Код в файлах заголовков всегда должен содержать полное имя в пространстве имен.
В следующем примере показано объявление пространства имен и продемонстрированы три способа доступа к членам пространства имен из кода за его пределами.
namespace ContosoData < class ObjectManager < public: void DoSomething() <>>; void Func(ObjectManager) <> >
Использование полного имени:
ContosoData::ObjectManager mgr; mgr.DoSomething(); ContosoData::Func(mgr);
Чтобы добавить в область видимости один идентификатор, используйте объявление using:
using ContosoData::ObjectManager; ObjectManager mgr; mgr.DoSomething();
Чтобы добавить в область видимости все идентификаторы пространства имен, используйте директиву using:
using namespace ContosoData; ObjectManager mgr; mgr.DoSomething(); Func(mgr);
Директивы using
Директива using позволяет использовать все имена в объекте namespace без имени пространства имен в качестве явного квалификатора. Используйте директиву using в файле реализации (т. е. *.cpp), если используется несколько различных идентификаторов в пространстве имен; Если вы используете только один или два идентификатора, рассмотрите возможность использования объявления, чтобы использовать только эти идентификаторы в область, а не все идентификаторы в пространстве имен. Если локальная переменная имеет такое же имя, как и переменная пространства имен, то переменная пространства имен будет скрытой. Создавать переменную пространства имен с те же именем, что и у глобальной переменной, является ошибкой.
Директиву using можно поместить в верхнюю часть CPP-файла (в области видимости файла) или внутрь определения класса или функции.
Без особой необходимости не размещайте директивы using в файлах заголовков (*.h), так как любой файл, содержащий этот заголовок, добавит все идентификаторы пространства имен в область видимости, что может вызвать скрытие или конфликты имен, которые очень трудно отлаживать. В файлах заголовков всегда используйте полные имена. Если эти имена получаются слишком длинными, используйте псевдоним пространства имен для их сокращения. (См. ниже.)
Объявление пространств имен и их членов
Как правило, пространство имен объявляется в файле заголовка. Если реализации функций находятся в отдельном файле, определяйте имена функций полностью, как показано в следующем примере.
// contosoData.h #pragma once namespace ContosoDataServer
Реализации функций в contosodata.cpp должны использовать полное имя, даже если директива using размещается в верхней части файла:
#include "contosodata.h" using namespace ContosoDataServer; void ContosoDataServer::Foo() // use fully-qualified name here < // no qualification needed for Bar() Bar(); >int ContosoDataServer::Bar()
Пространство имен может быть объявлено в нескольких блоках в одном файле и в нескольких файлах. Компилятор соединит вместе все части во время предварительной обработки и полученное в результате пространство имен будет содержать все члены, объявленные во всех частях. Примером этого является пространство имен std, которое объявляется в каждом из файлов заголовка в стандартной библиотеке.
Члены именованного пространства имен можно определить вне пространства имен, в котором они объявлены явным определением имени. Однако определение должно располагаться после точки объявления в пространстве имен, окружающем то пространство имен, где находится объявление. Например:
// defining_namespace_members.cpp // C2039 expected namespace V < void f(); >void V::f() < >// ok void V::g() < >// C2039, g() is not yet a member of V namespace V
Эта ошибка может возникнуть, когда члены пространства имен объявляются в нескольких файлах заголовка и эти заголовки не включены в правильном порядке.
Глобальное пространство имен
Если идентификатор не объявлен явно в пространстве имен, он неявно считается входящим в глобальное пространство имен. Как правило, старайтесь избегать объявления в глобальном область, если это возможно, за исключением основной функции точки входа, которая должна находиться в глобальном пространстве имен. Чтобы явно указать глобальный идентификатор, используйте оператор разрешения области видимости без имени, как сделано в ::SomeFunction(x); . Это позволит отличать данный идентификатор от любого другого элемента с таким же именем, находящегося в другом пространстве имен. Кроме того, это облегчит понимание кода.
Пространство имен std
Все стандартные типы и функции библиотек C++ объявляются в std пространстве имен или пространствах имен, вложенных внутри std .
Вложенные пространства имен
Пространства имен могут быть вложенными. Обычное вложенное пространство имен имеет неквалифицированный доступ к членам родительского элемента, но родительские члены не имеют неквалифицированного доступа к вложенному пространству имен (если он не объявлен как встроенный), как показано в следующем примере:
namespace ContosoDataServer < void Foo(); namespace Details < int CountImpl; void Ban() < return Foo(); >> int Bar(); int Baz(int i) < return Details::CountImpl; >>
Обычные вложенные пространства имен можно использовать для инкапсуляции данных о внутренней реализации, которые не являются частью открытого интерфейса родительского пространства имен.
Встроенные пространства имен (C++11)
В отличие от обычных вложенных пространств имен члены встроенного пространства имен обрабатываются как члены родительского пространства имен. Эта особенность позволяет выполнять поиск перегруженных функций с зависимостью от аргументов среди функции, которые имеют перегрузки в родительском и вложенном встроенном пространстве имен. Это также позволяет объявлять специализации в родительском пространстве имен для шаблонов, объявленных во встроенном пространстве имен. В следующем примере показано, как внешний код привязывается к встроенному пространству имен по умолчанию.
// Header.h #include namespace Test < namespace old_ns < std::string Func() < return std::string("Hello from old"); >> inline namespace new_ns < std::string Func() < return std::string("Hello from new"); >> > // main.cpp #include "header.h" #include #include int main() < using namespace Test; using namespace std; string s = Func(); std::cout
В следующем примере показано, как можно объявить специализацию в родительском пространстве имен шаблона, объявленного во встроенном пространстве имен.
namespace Parent < inline namespace new_ns < template struct C < T member; >; > template<> class C <>; >
Встроенные пространства имен можно использовать как механизм управления версиями для управления изменениями в открытом интерфейсе библиотеки. Например, можно создать одно родительское пространство имен и инкапсулировать каждую версию интерфейса в своем собственном пространстве имен, вложенном в родительское. Пространство имен, которое содержит самую последнюю или основную версию, квалифицируется как встроенное и поэтому представляется так, будто оно является непосредственным членом родительского пространства имен. Клиентский код, вызывающий Parent::Class, автоматически привязывается к новому коду. Клиенты, которые предпочитают использовать старую версию, могут по-прежнему получить доступ к ней, используя полный путь к вложенному пространству имен, содержащему данный код.
Ключевое слово inline должно применяться к первому объявлению пространства имен в единице компиляции.
В следующем примере показано две версии интерфейса: каждое — во вложенном пространстве имен. Пространство имен v_20 содержит некоторые изменения из интерфейса v_10 и помечается как встроенное. Клиентский код, который использует новую библиотеку и вызывает Contoso::Funcs::Add , вызовет версию v_20. Код, который пытается вызвать Contoso::Funcs::Divide , теперь будет вызывать ошибку времени компиляции. Если действительно требуется эта функция, доступ к версии v_10 можно получить путем явного вызова Contoso::v_10::Funcs::Divide .
namespace Contoso < namespace v_10 < template class Funcs < public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); T Divide(T a, T b); >; > inline namespace v_20 < template class Funcs < public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); std::vectorLog(double); T Accumulate(std::vector nums); >; > >
Псевдонимы пространств имен
Имена пространств имен должны быть уникальными, из-за чего зачастую они получаются не слишком короткими. Если длина имени затрудняет чтение кода или утомительно вводить в файл заголовка, где нельзя использовать директивы, можно сделать псевдоним пространства имен, который служит сокращенным для фактического имени. Например:
namespace a_very_long_namespace_name < class Foo <>; > namespace AVLNN = a_very_long_namespace_name; void Bar(AVLNN::Foo foo)
анонимные или безымянные пространства имен
Вы можете создать явное пространство имен, но не присвоить ему имя.
namespace < int MyFunc()<>>
Это называется неименованным или анонимным пространством имен, и это полезно, если вы хотите сделать объявления переменных невидимыми для кода в других файлах (т. е. дать им внутреннюю компоновку), не создавая именованное пространство имен. Весь код, находящийся в том же файле, может видеть идентификаторы в безымянном пространстве имен, но эти идентификаторы, а также само пространство имен, будет невидимым за пределами этого файла или, точнее, вне блока перевода.
Урок №54. using-стейтменты
Если вы часто используете Стандартную библиотеку C++, то постоянное добавление std:: к используемым объектам может быть несколько утомительным, не правда ли? Язык C++ предоставляет альтернативы в виде using-стейтментов.
Оглавление:
- Использование «using-объявления»
- Использование «using-директивы»
- Пример конфликта c «using-директивой»
- Область видимости «using-объявления» и «using-директивы»
- Отмена/замена using-стейтментов
Использование «using-объявления»
Одной из альтернатив является использование «using-объявления». Вот программа «Hello, world!» с «using-объявлением» в строке №5:
using std :: cout ; // "using-объявление" сообщает компилятору, что cout следует обрабатывать, как std::cout
cout << "Hello, world!" ; // и никакого префикса std:: уже здесь не нужно!
Строка using std::cout; сообщает компилятору, что мы будем использовать объект cout из пространства имен std. И каждый раз, когда компилятор будет сталкиваться с cout , он будет понимать, что это std::cout .
Конечно, в этом случае мы не сэкономили много усилий, но в программе, где объекты из пространства имен std используются сотни, если не тысячи раз, «using-объявление» неплохо так экономит время, усилия и улучшает читабельность кода. Также для каждого объекта нужно использовать отдельное «using-объявление» (например, отдельное для std::cout , отдельное для std::cin и отдельное для std::endl ).
Хотя этот способ является менее предпочтительным, чем использование префикса std:: , он все же является абсолютно безопасным и приемлемым.
Использование «using-директивы»
Второй альтернативой является использование «using-директивы». Вот программа «Hello, world!» с «using-директивой» в строке №5:
using namespace std ; // "using-директива" сообщает компилятору, что мы используем все объекты из пространства имен std!
cout << "Hello, world!" ; // так что никакого префикса std:: здесь уже не нужно!
Много разработчиков спорят насчет использования «using-директивы». Так как с её помощью мы подключаем ВСЕ имена из пространства имен std, то вероятность возникновения конфликтов имен значительно возрастает (но все же эта вероятность в глобальном масштабе остается незначительной). using namespace std; сообщает компилятору, что мы хотим использовать всё, что находится в пространстве имен std, так что, если компилятор найдет имя, которое не сможет распознать, он будет проверять его наличие в пространстве имен std.
Совет: Старайтесь избегать использования «using-директивы» (насколько это возможно).
Пример конфликта c «using-директивой»
Рассмотрим пример, где использование «using-директивы» создает неопределенность:
int cout ( ) // объявляем нашу собственную функцию "cout"
using namespace std ; // делаем std::cout доступным по "cout"
cout
Здесь компилятор не сможет понять, использовать ли ему std::cout или функцию cout(), которую мы определили сами. В результате, получим ошибку неоднозначности. Хоть это и банальный пример, но если бы мы добавили префикс std:: к cout:
std :: cout << "Hello, world!" ; // сообщаем компилятору, что хотим использовать std::cout
Или использовали бы «using-объявление» вместо «using-директивы»:
using std :: cout ; // сообщаем компилятору, что cout означает std::cout
cout << "Hello, world!" ; // так что здесь следует использовать std::cout
Тогда наша программа была бы без ошибок.
Большинство программистов избегают использования «using-директивы» именно по этой причине. Другие считают это приемлемым до тех пор, пока «using-директива» используется только в пределах отдельных функций (что значительно сокращает масштабы возникновения конфликтов имен).
Области видимости «using-объявления» и «using-директивы»
Если «using-объявление» или «using-директива» используются в блоке, то они применяются только внутри этого блока (по обычным правилам локальной области видимости). Это хорошо, поскольку уменьшает масштабы возникновения конфликтов имен до отдельных блоков. Однако многие начинающие программисты пишут «using-директиву» в глобальной области видимости (вне функции main() или вообще вне любых функций). Этим они вытаскивают все имена из пространства имен std напрямую в глобальную область видимости, значительно увеличивая вероятность возникновения конфликтов имен. А это уже не хорошо.
Правило: Никогда не используйте using-стейтменты вне тела функций.
Отмена/замена using-стейтментов
Как только один using-стейтмент был объявлен, его невозможно отменить или заменить другим using-стейтментом в пределах области видимости, в которой он был объявлен. Например:
Using c что это
При чтении и записи в предыдущих темах использовались объекты std::cout и std::cin соответственно. Причем они использовались с префиксом std:: . Этот префикс указывает, что объекты cout, cin, endl определены в пространстве имен std . А само двойное двоеточие :: представляет оператор области видимости (scope operator), который позволяет указать, в каком пространстве имен определен объект. И без префикса эти объекты по умолчанию мы использовать не можем.
Однако подобная запись может показаться несколько громоздкой. И в этом случае можно использовать оператор using , который позволяет ввести в программу объекты из различных пространств имен.
Использование оператора using имеет следующий формат:
using пространство_имен::объект
Например, пусть у нас есть следующая программа:
#include int main() < int age; std::cout > age; std::cout
Здесь используются сразу три объекта из пространства имен std : cout , cin и endl . Перепишем программу с использованием using:
#include using std::cin; using std::cout; using std::endl; int main() < int age; cout > age; cout
Для каждого объекта из пространства std определяется свое выражение using. При этом программа будет работать также как и раньше.
Определение псевдонимов
Ключевое слово using также позволяет определять псевдонимы для типов. Это может пригодиться, когда мы работаем с типами с длинными названиями, а определение коротких псевдонимов позволит сократить код. Например:
#include using ullong = unsigned long long; int main() < ullong n ; std::cout
В данном случае для типа unsigned long long определен псевдоним ullong . Стоит отметить, что это именно определение псевдонима, а НЕ определение нового типа.
Стоит отметить, что для определения псевдонимов в С++ также может использоваться старый подход в стиле языка С с помощью оператора typedef :
#include typedef unsigned long long ullong; int main() < ullong n ; std::cout