-->

Каково предназначение инструкции using в начале кодового файла


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters

Show hidden characters

1. Как в языке C# называют именованную последовательность инструкций 1 из 1 балла
Метод
2. Какие утверждения верны? 1 из 1 балла
Сборка — это как правило результат компиляции проекта
Solution (решение) может содержать несколько проектов
В проекте может быть более одного кодового файла
Разные проекты одного решения могут содержать классы в одном и том же пространстве имён
3. Что перечисляется в секции References (ссылки) проекта в Visual Studio (или других IDE) 0 из 1 балла
Сборки, классы которых доступны для использования в кодовых файлах проекта
4. Каково предназначение инструкции using в начале кодового файла? 1 из 1 балла
Избавляет программиста от необходимости указывать пространство имён перед именами классов данного пространства имён, сокращая код
5. Где найти exe-файл — результат компиляции моего проекта 1 из 1 балла
Скорее всего в подпапке bin/Debug папки вашего проекта
На самом деле путь можно настроить в свойствах проекта (контекстное меню на проекте)

Добавлено 15 мая 2021 в 20:50

Вы, наверное, видели эту программу во многих учебниках и учебных руководствах:

#include <iostream>
 
using namespace std;
 
int main()
{
    cout << "Hello world!";
 
    return 0;
}

Некоторые старые компиляторы также начинают новые проекты с аналогичной программы.

Если вы это видите, бегите. Возможно, ваш учебник, руководство или компилятор устарели. В этом уроке мы выясним, почему.

Краткий урок истории

Еще до того, как C++ получил поддержку пространств имен, все имена, которые сейчас присутствуют в пространстве имен std, были в глобальном пространстве имен. Это вызывало конфликты имен между идентификаторами программ и идентификаторами стандартной библиотеки. Программы, которые работали с одной версией C++, могли получить конфликт имен с более новой версией C++.

В 1995 году пространства имен были стандартизированы, и все функции стандартной библиотеки были перенесены из глобального пространства имен в пространство имен std. Это изменение сломало старый код, в котором всё еще использовались имена без std::.

Любой, кто работал с большой кодовой базой, знает, что любое изменение кодовой базы (каким бы тривиальным оно ни было) может нарушить работу программы. Обновление каждого имени, которое теперь было перемещено в пространство имен std для использования префикса std::, было огромным риском. Было запрошено решение.

Перенесемся в сегодняшний день – если вы много используете стандартную библиотеку, набирание std:: перед всем, что вы используете из стандартной библиотеки, может стать повторяющейся рутиной, а в некоторых случаях может затруднить чтение вашего кода.

C++ предлагает решение обеих этих проблем в виде инструкций using.

Но сначала давайте определим два термина.

Полные и неполные имена

Имя может быть полным или неполным.

Полное имя – это имя, которое включает в себя связанную область видимости. Чаще всего имена дополняются пространством имен с помощью оператора разрешения области видимости (::). Например:

std::cout // идентификатор cout уточняется с помощью пространства имен std
::foo     // идентификатор foo уточняется с помощью глобального пространства имен

Для продвинутых читателей


Имя также может быть уточнено именем класса с помощью оператора разрешения области (::) или объектом класса с помощью операторов выбора члена (. или ->). Например:

class C;     // какой-то класс
 
C::s_member; // s_member уточняется с помощью класса C
obj.x;       // x уточняется с помощью объекта класса obj
ptr->y;      // y уточняется с помощью указателя на объект класса ptr

Неполное имя – это имя, которое не включает в себя квалификатор области видимости. Например, cout и x являются неполными именами, поскольку они не включают связанную область видимости.

Объявления using

Один из способов уменьшить повторение ввода std:: снова и снова – использовать инструкцию объявления using. Объявление using позволяет нам использовать неполное имя (без области видимости) в качестве псевдонима для полного имени.

Вот наша базовая программа Hello world, в которой используется объявление using в строке 5:

#include <iostream>
 
int main()
{
   using std::cout;        // это объявление using сообщает компилятору,
                           // что cout должен вычисляться как std::cout
   cout << "Hello world!"; // поэтому префикс std:: здесь не нужен!
 
   return 0;
} // здесь истекает срок действия объявления using

Объявление using с использованием std::cout; сообщает компилятору, что мы собираемся использовать объект cout из пространства имен std. Поэтому всякий раз, когда он видит cout, он предполагает, что мы имеем в виду std::cout. Если существует конфликт имен между std::cout и каким-либо другим использованием cout, предпочтительнее использовать std::cout. Поэтому в строке 6 мы можем ввести cout вместо std::cout.

В этом тривиальном примере это не сэкономит много усилий, но если вы много раз используете cout внутри функции, объявление using может сделать ваш код более читабельным. Обратите внимание, что вам потребуется отдельное объявление using для каждого имени (например, одно для std::cout, одно для std::cin и т.д.).

Хотя этот метод менее явный, чем использование префикса std::, он обычно считается безопасным и приемлемым (при использовании внутри функции).

Директивы using

Другой способ упростить эту задачу – использовать директиву using. Директива using импортирует все идентификаторы из пространства имен в область видимости директивы using.

Вот снова наша программа Hello World с директивой using в строке 5:

#include <iostream>
 
int main()
{
   using namespace std;    // эта директива using указывает компилятору импортировать
                           // все без уточнения имена из пространства имен std в
                           // текущее пространство имен
   cout << "Hello world!"; // поэтому префикс std:: здесь не нужен
   return 0;
}

Директива using namespace std; указывает компилятору импортировать все имена из пространства имен std в текущую область видимости (в данном случае в функцию main()). Затем, когда мы используем неполный идентификатор cout, он будет преобразован в импортированный std::cout.

Директивы using – это решение, которое было предоставлено для баз старого кода до пространства имен, которые использовали неполные имена для функций стандартной библиотеки. Вместо того, чтобы вручную обновлять каждое неполное имя до полного имени (что было рискованно), можно было бы разместить одну директиву using (т.е. using namespace std;) в верхней части каждого файла, и все имена, которые были перемещены в пространство имен std всё еще можно было использовать неполными.

Проблемы с директивами using

Однако для современного кода C++ директивы using дают немного выгоды (экономия на вводе текста) по сравнению с риском.

Поскольку директивы using импортируют все имена из пространства имен (потенциально включая множество имен, которые вы никогда не будете использовать), вероятность возникновения конфликтов имен значительно возрастает (особенно, если вы импортируете пространство имен std).

Для наглядности рассмотрим пример, в котором директивы using вызывают неоднозначность:

#include <iostream>
 
namespace a
{
	int x{ 10 };
}
 
namespace b
{
	int x{ 20 };
}
 
int main()
{
	using namespace a;
	using namespace b;
 
	std::cout << x << 'n';
 
	return 0;
}

В приведенном выше примере компилятор не может определить, относится ли x в main к a::x или b::x. В этом случае код не будет скомпилирован из-за ошибки «ambiguous symbol» (неоднозначный символ). Мы могли бы решить эту проблему, удалив одну из инструкций using, используя вместо нее объявление using или определив x с явным квалификатором области видимости (a:: или b::).

Вот еще один более коварный пример:

#include <iostream> // импортирует объявление std::cout
 
int cout() // объявляет нашу собственную функцию "cout"
{
    return 5;
}
 
int main()
{
    using namespace std;     // делает std::cout доступным как "cout"
    cout << "Hello, world!"; // упс! Какой cout нам нужен здесь?
                             // Тот, который находится в пространстве имен std или тот,
                             // который мы определили выше?
 
    return 0;
}

В приведенном выше примере компилятор не может определить, наше использование 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 для какой-либо библиотеки, которая затем обновляется, все новые имена, представленные в обновленной библиотеке, теперь являются кандидатами на конфликты имен с вашим существующим кодом.

Может возникнуть и более коварная проблема. В обновленной библиотеке может появиться функция, которая не только имеет то же имя, но и лучше подходит для некоторых вызовов функций. В таком случае компилятор может решить отдать предпочтение новой функции, и поведение вашей программы неожиданно изменится.

Рассмотрим следующую программу:

foolib.h:

namespace Foo
{
    // притворимся, что здесь есть полезный код
}

main.cpp:

#include <iostream>
#include <foolib.h>
 
int someFcn(double)
{
    return 1;
}
 
int main()
{
    using namespace Foo;     // Потому что мы ленивы и хотим получить доступ к именам
                             // с квалификатором Foo:: без ввода префикса Foo::
    std::cout << someFcn(0); // Литерал 0 должен быть 0.0, но эту ошибку легко допустить
 
    return 0;
}

Эта программа запускается и печатает 1.

Теперь предположим, что мы обновляем библиотеку foolib, которая включает обновленный foolib.h. Наша программа теперь выглядит так:

foolib.h:

namespace Foo
{
    // добавленная недавно функция
    int someFcn(int)
    {
        return 2;
    }
 
    // притворимся, что здесь есть полезный код
}

main.cpp:

#include <iostream>
#include <foolib.h>
 
int someFcn(double)
{
    return 1;
}
 
int main()
{
    using namespace Foo;     // Потому что мы ленивы и хотим получить доступ к именам
                             // с квалификатором Foo:: без ввода префикса Foo::
    std::cout << someFcn(0); // Литерал 0 должен быть 0.0, но эту ошибку легко допустить
 
    return 0;
}

Наш файл main.cpp вообще не изменился, но теперь эта программа запускается и выводит 2!

Когда компилятор встречает вызов функции, он должен определить, с каким определением функции он должен сопоставить этот вызов. При выборе функции из набора потенциально совпадающих функций он предпочтет ту функцию, которая не требует преобразования аргументов, а не ту, которая требует преобразования аргументов. Поскольку литерал 0 принадлежит целочисленному типу, C++ предпочтет сопоставить someFcn(0) с недавно добавленной someFcn(int) (без преобразований), а не с someFcn(double) (требуется преобразование из int в double). Это вызывает неожиданное изменение результатов работы нашей программы.

Этого бы не произошло, если бы мы использовали объявление using или явный квалификатор области видимости.

Область видимости объявлений и директив using

Если объявление using или директива using используется в блоке, имена применимы только в этом блоке (они следуют обычным правилам области видимости блока). Это хорошо, поскольку снижает вероятность возникновения конфликтов имен внутри этого блока.

Если объявление using или директива using используются в глобальном пространстве имен, имена применимы ко всему остальному файлу (они имеют область видимости файла).

Отмена или замена инструкции using

После объявления инструкции using ее невозможно отменить или заменить ее другой инструкцией using в той области видимости, в которой она была объявлена.

int main()
{
    using namespace Foo;
 
    // здесь нет способа отменить "using namespace Foo"!
    // также нет возможности заменить "using namespace Foo" другой инструкцией using
 
    return 0;
} // "using namespace Foo" здесь прекращает свое действие

Лучшее, что вы можете сделать, – это намеренно с самого начала ограничить область видимости инструкции using с помощью правил области видимости блока.

int main()
{
    {
        using namespace Foo;
        // здесь вызовы функций из Foo::
    } // "using namespace Foo" прекращает свое действие
 
    {
        using namespace Goo;
        // здесь вызовы функций из Goo::
    } // "using namespace Goo" прекращает свое действие
 
    return 0;
}

Конечно, всей этой головной боли можно избежать, в первую очередь, используя явным образом оператор разрешения области видимости (::).

Лучшие практики для инструкций using

В современном C++ на самом деле нет места для использования директив. Они увеличивают вероятность коллизий имен сейчас и в будущем и могут вызывать более коварные проблемы. Хотя во многих учебниках и руководствах они широко используются, использования директив лучше вообще избегать.

Объявления using обычно считаются безопасными для использования внутри блоков. Ограничьте их использование в глобальном пространстве имен файлов исходного кода и никогда не используйте их в глобальном пространстве имен заголовочных файлов.

Лучшая практика


Предпочитайте явное указание пространств имен вместо инструкций using. Полностью избегайте директив using. Внутри блоков допускается использование объявлений using.

Теги

C++ / CppLearnCppnamespaceusingДля начинающихОбучениеПрограммированиеПространство имен

В данной статье будет рассказано о том, что собой представляет пространство имен в C#. Предстоит познакомиться с директивами типа using, а также рассмотреть наглядные примеры использования упомянутых компонентов.

Директива – это…

Директива – это указание. Определение соответствующего понятия в программировании напоминает термин «команда». Он используется для описания некоторых конструкций ЯП. Указывает, как компилятор или любой иной транслятор должен обрабатывать вводимые данные.

Директивы не являются частью грамматики. Они могут меняться в зависимости от компилятора. Обрабатываются процессором для определения поведения компилятора, а также функционируют в виде своеобразной функции внутренней параметризации.

Иногда подобные компоненты отвечают за определение глобального поведения, а в некоторых ситуациях действуют локально. В программах на C не являются обязательными. Они могут быть проигнорированы компилятором. Чаще всего носят предписывающий характер. В самом языке директивы (using и не только) не отвечают за выполнение тех или иных действий ЯП – они лишь корректируют поведение компиляторов.

Пространства имен – что это

Пространства имен (namespace) – это то, что позволяет выделить определенные части приложений в логические блоки. Поддерживается работа с:

  • классами;
  • интерфейсами;
  • прочими элементами программного кода.

Подобная концепция позволяет сделать исходный код более читабельным и понятным. Пространства имен применяются специально для организации классов. Помогают контролировать область применения методов в более крупных проектах.

При помощи упомянутого элемента можно сохранить один набор имен (пример – имена классов) отличным от других наборов имен. Преимуществом такого подхода является то, что имена классов, объявленные в пределах одного пространства, не конфликтуют с аналогичными, но в других пространствах.

Членами могут выступать:

  • пространства имен;
  • структуры;
  • делегаты.

Пространства имен можно охарактеризовать как группу классов, обладающих общими признаками.

Свойства

Перед тем как изучить директиву using, требуется запомнить следующие свойства именных пространств:

  • организация масштабных проектов по созданию программных кодов;
  • разделение происходит при помощи оператора «.» (точки);
  • global – корневая область имен.

Using – директива, которая позволяет исключить требование на указание названия области имен для каждого класса. Данный компонент избавляет от полной классификации «названий». Далее соответствующий элемент будет рассмотрен более подробно.

Using – описание

Директивы using – это то, что производит импорт именных областей, избавляя разработчика от полной классификации имен стереотипов. Дает возможность задействовать определенные типы без указания полной области «названий» соответствующего типа. В базовой форме used используется импорт всех типов именных пространств.

Using directive поддерживает применение двух модификаторов:

  1. Модификатор global. Он будет работать точно так, как и добавление одной директивы use к каждому исходному документу в пределах проекта. Первое появление состоялось в C Sharp 10 версии.
  2. Модификатор static. Он производит импорт элементов static, а также вложенных типов из одного типа, а не из всех в пределах области «названий».

Оба модификатора можно использовать вместе для импорта статистических членов из того или иного типа во всех исходных проектных файлах.

Сейчас C# поддерживает создание псевдонимов для namespace. Для этого используется директива псевдонимов using:

using Project = PC.MyCompany.Project;

Модификатор global используется в using alias. Без него область будет ограничиваться файлом, в пределах которого находится директива.

Особенности

Директива using в C# имеет ряд особенностей. Она отображается в нескольких местах – все зависит от ситуации:

  1. Начало документа в исходном коде. Перед тем, как объявлять именные «области», а также типы.
  2. В любом namespace, но до пространственных имен, объявленных в первой «области». Здесь обязательно, чтобы не использовался модификатор global. Если он присутствует, директива using располагается перед всеми объявлениями.

Если не соблюдать данные принципы, при попытке обработки кода появится ошибка компиляции CS1529.

Директива using создается для того, чтобы использовать типы в пространстве имен без необходимости указания его самого. Если использовать using System, больше не придется вводить полный путь. Методы в приложении получится вызывать быстрее и короче.

Using – это ключевое слово, которое в C# имеет широкое применение. Оно используется для создания операторов using. Данные элементы используются для грамотной обработки объектов IDisposable – шрифтов и файлов.

Псевдонимы

Using alias – это директива, позволяющая создавать псевдонимы для namespaces. У нее не может быть открытого универсального типа в правой части. Создать его для List<T> не получится, но можно для List<int>.

Если отсутствует импорт пространств имен System, полные имена базовых типов окажутся недоступными:

Для подключения using alias потребуется:

  1. Добавить в References желаемые сборки.
  2. Открыть соответствующую папку и кликнуть по сборке правой кнопкой мышки.
  3. Выбрать раздел Properties.
  4. Нажать на свойство Aliases.

После этого остается заменить значение global на свое собственное название.

Наглядный пример

Для того, чтобы хорошо разбираться в директивах и их uses, стоит изучить наглядный пример. Ниже приведен фрагмент кода. Он будет взять за «базу» для изучения directive и оператора:

Здесь в операторе using в круглых скобках написано выражение, обработка которого приводит к получению специального объекта. Он будет реализовывать интерфейс System IDisposable. Если объект не реализует интерфейс, на экране появится сообщение об ошибке.

В приведенном фрагменте объект определяется за пределами оператора using. Соответствующий код может быть записан так:

После обработки блока using в C происходит автоматический вызов метода Dispose(). Он осуществляется для переданного ранее объекта и располагается в интерфейсе System.IDisposable.

При использовании потоков необходимо запомнить – при завершении оперирования ими требуется завершать соответствующие процессы. Аналогичные принципы применяются к базам данных, сокетам, иным источникам. Метод Dispose() будет каждый раз завершать работу с потоком и высвобождать ресурсы.

А вот более простой фрагмент программы:

Здесь:

  1. Using namespace std указывает компилятору на импорт всех имен из пространства std в текущую область видимости. В приведенном примере таковым является main().
  2. После использования неполного идентификатора count происходит преобразование в std::count.

Данный вариант подойдет для того, чтобы упрощать программный код. С ним не придется вручную прописывать и обновлять все неполные имена до полных.

Как лучше освоить

C# – язык, который пользуется спросом у разработчиков. Для того, чтобы хорошенько изучить его директивы, а также пространства имен и иные элементы, можно заняться самообразованием. Это не лучшее решение – обучение может затянуться, а доказать полученные навыки и знания документально окажется невозможно. Именно поэтому рекомендуется отдать предпочтение дистанционным онлайн курсам.

Они рассчитаны на широкую публику и прекрасно совмещаются со всеми делами – уроки можно просматривать в любое время. Курсы рассчитаны на срок до 12 месяцев. Пользователи получают шанс выбрать одновременно одно или несколько инновационных IT-направлений для изучения. Процесс сопровождается домашними заданиями и богатой практикой. В конце обучения выдается электронный сертификат, подтверждающий приобретенный спектр навыков и знаний.

Хотите освоить современную IT-специальность? Огромный выбор курсов по востребованным IT-направлениям есть в Otus!

description title ms.date f1_keywords helpviewer_keywords ms.assetid

using directive — C# Reference

using directive — C# Reference

08/19/2021

using_CSharpKeyword

using directive [C#]

b42b8e61-5e7e-439c-bb71-370094b44ae8

The using directive allows you to use types defined in a namespace without specifying the fully qualified namespace of that type. In its basic form, the using directive imports all the types from a single namespace, as shown in the following example:

You can apply two modifiers to a using directive:

  • The global modifier has the same effect as adding the same using directive to every source file in your project. This modifier was introduced in C# 10.
  • The static modifier imports the static members and nested types from a single type rather than importing all the types in a namespace.

You can combine both modifiers to import the static members from a type in all source files in your project.

You can also create an alias for a namespace or a type with a using alias directive.

using Project = PC.MyCompany.Project;

You can use the global modifier on a using alias directive.

[!NOTE]
The using keyword is also used to create using statements, which help ensure that xref:System.IDisposable objects such as files and fonts are handled correctly. For more information about the using statement, see using statement.

The scope of a using directive without the global modifier is the file in which it appears.

The using directive can appear:

  • At the beginning of a source code file, before any namespace or type declarations.
  • In any namespace, but before any namespaces or types declared in that namespace, unless the global modifier is used, in which case the directive must appear before all namespace and type declarations.

Otherwise, compiler error CS1529 is generated.

Create a using directive to use the types in a namespace without having to specify the namespace. A using directive doesn’t give you access to any namespaces that are nested in the namespace you specify. Namespaces come in two categories: user-defined and system-defined. User-defined namespaces are namespaces defined in your code. For a list of the system-defined namespaces, see .NET API Browser.

global modifier

Adding the global modifier to a using directive means that using is applied to all files in the compilation (typically a project). The global using directive was added in C# 10. Its syntax is:

global using <fully-qualified-namespace>;

where fully-qualified-namespace is the fully qualified name of the namespace whose types can be referenced without specifying the namespace.

A global using directive can appear at the beginning of any source code file. All global using directives in a single file must appear before:

  • All using directives without the global modifier.
  • All namespace and type declarations in the file.

You may add global using directives to any source file. Typically, you’ll want to keep them in a single location. The order of global using directives doesn’t matter, either in a single file, or between files.

The global modifier may be combined with the static modifier. The global modifier may be applied to a using alias directive. In both cases, the directive’s scope is all files in the current compilation. The following example enables using all the methods declared in the xref:System.Math?displayProperty=fullName in all files in your project:

global using static System.Math;

You can also globally include a namespace by adding a <Using> item to your project file, for example, <Using Include="My.Awesome.Namespace" />. For more information, see <Using> item.

[!INCLUDE csharp10-templates]

static modifier

The using static directive names a type whose static members and nested types you can access without specifying a type name. Its syntax is:

using static <fully-qualified-type-name>;

The <fully-qualified-type-name> is the name of the type whose static members and nested types can be referenced without specifying a type name. If you don’t provide a fully qualified type name (the full namespace name along with the type name), C# generates compiler error CS0246: «The type or namespace name ‘type/namespace’ couldn’t be found (are you missing a using directive or an assembly reference?)».

The using static directive applies to any type that has static members (or nested types), even if it also has instance members. However, instance members can only be invoked through the type instance.

You can access static members of a type without having to qualify the access with the type name:

using static System.Console;
using static System.Math;
class Program
{
    static void Main()
    {
        WriteLine(Sqrt(3*3 + 4*4));
    }
}

Ordinarily, when you call a static member, you provide the type name along with the member name. Repeatedly entering the same type name to invoke members of the type can result in verbose, obscure code. For example, the following definition of a Circle class references many members of the xref:System.Math class.

:::code language=»csharp» source=»./snippets/using-static1.cs» id=»Snippet1″:::

By eliminating the need to explicitly reference the xref:System.Math class each time a member is referenced, the using static directive produces cleaner code:

:::code language=»csharp» source=»./snippets/using-static2.cs»:::

using static imports only accessible static members and nested types declared in the specified type. Inherited members aren’t imported. You can import from any named type with a using static directive, including Visual Basic modules. If F# top-level functions appear in metadata as static members of a named type whose name is a valid C# identifier, then the F# functions can be imported.

using static makes extension methods declared in the specified type available for extension method lookup. However, the names of the extension methods aren’t imported into scope for unqualified reference in code.

Methods with the same name imported from different types by different using static directives in the same compilation unit or namespace form a method group. Overload resolution within these method groups follows normal C# rules.

The following example uses the using static directive to make the static members of the xref:System.Console, xref:System.Math, and xref:System.String classes available without having to specify their type name.

:::code language=»csharp» source=»./snippets/using-static3.cs» id=»Snippet1″:::

In the example, the using static directive could also have been applied to the xref:System.Double type. Adding that directive would make it possible to call the xref:System.Double.TryParse(System.String,System.Double@) method without specifying a type name. However, using TryParse without a type name creates less readable code, since it becomes necessary to check the using static directives to determine which numeric type’s TryParse method is called.

using static also applies to enum types. By adding using static with the enum, the type is no longer required to use the enum members.

using static Color;

enum Color
{
    Red,
    Green,
    Blue
}

class Program
{
    public static void Main()
    {
        Color color = Green;
    }
}

using alias

Create a using alias directive to make it easier to qualify an identifier to a namespace or type. In any using directive, the fully qualified namespace or type must be used regardless of the using directives that come before it. No using alias can be used in the declaration of a using directive. For example, the following example generates a compiler error:

using s = System.Text;
using s.RegularExpressions; // Generates a compiler error.

The following example shows how to define and use a using alias for a namespace:

:::code language=»csharp» source=»./snippets/csrefKeywordsNamespace2.cs» id=»Snippet8″:::

A using alias directive can’t have an open generic type on the right-hand side. For example, you can’t create a using alias for a List<T>, but you can create one for a List<int>.

The following example shows how to define a using directive and a using alias for a class:

:::code language=»csharp» source=»./snippets/csrefKeywordsNamespace2.cs» id=»Snippet9″:::

Beginning with C# 12, you can create aliases for types that were previously restricted, including tuple types, pointer types, and other unsafe types. For more information on the udpated rules, see the feature spec.

How to use the Visual Basic My namespace

The xref:Microsoft.VisualBasic.MyServices namespace (My in Visual Basic) provides easy and intuitive access to a number of .NET classes, enabling you to write code that interacts with the computer, application, settings, resources, and so on. Although originally designed for use with Visual Basic, the MyServices namespace can be used in C# applications.

For more information about using the MyServices namespace from Visual Basic, see Development with My.

You need to add a reference to the Microsoft.VisualBasic.dll assembly in your project. Not all the classes in the MyServices namespace can be called from a C# application: for example, the xref:Microsoft.VisualBasic.MyServices.FileSystemProxy class is not compatible. In this particular case, the static methods that are part of xref:Microsoft.VisualBasic.FileIO.FileSystem, which are also contained in VisualBasic.dll, can be used instead. For example, here is how to use one such method to duplicate a directory:

:::code language=»csharp» source=»./snippets/Namespaces3.cs» id=»Snippet20″:::

C# language specification

For more information, see Using directives in the C# Language Specification. The language specification is the definitive source for C# syntax and usage.

For more information on the global using modifier, see the global usings feature specification — C# 10.

See also

  • C# reference
  • C# keywords
  • Namespaces
  • Style rule IDE0005 — Remove unnecessary ‘using’ directives
  • Style rule IDE0065 — ‘using’ directive placement
  • using statement

When I write code I don’t only want to write code that is correct. I also want to write code that is understandable, and maintainable. I want to deliver code that is easy to read not only for the compiler but also for other human beings. After all, humans will read my code more frequently than compilers.

I have been thinking what are the single most important keywords that help us write readable code. Probably this question doesn’t make much sense, but const and using are definitely among these. We already discussed const a lot, this time it’s time to see how using using can improve our code.

We are going to review the 4 ways we can use it:

  • type aliasing with using
  • introducing complete namespaces with using-directive
  • introducing members of another namespace with using-declaration
  • importing class members with using-declaration

Aliasing

In old C++ we could use typedef to give another name, to give an alias for our types. Sometimes you might want to use it instead of strong typing, just to benefit from more meaningful names like int.

1
typedef int Horsepower;

Other times you want to shorten long types for easier usage:

1
typedef std::vector<std::string>::iterator Iterator;

Since C++11 we can use using instead of typedef to achieve the same results.

1
2
using Horsepower = int;
using Iterator = std::vector<std::string>::iterator;

Why would you use using over the good old typedef? Just read the above statements! Exactly like the T.43 core guideline says, it’s more readable! The keyword has a very clear meaining, then the name comes first and the old comes after a =.

Besides, using can be used more generally. It can be used for template aliases where typedef would lead to a compilation error.

1
2
3
4
5
template<typename T>
typedef std::map<int, T> MapT;      // error

template<typename T>
using MapT = std::map<int, T>;   // OK

Using-directive in namespace and block scope

You’ve probably seen many code examples that right after the #include statements contain the line using namespace std.

You’ve probably seen lots of such application code.

You’ve probably been told that it’s bad.

It’s particularly bad if you do in at the global scope in a header file, just like [SF.7 from the Core Guidelines says]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// bad.h
#include <iostream>
using namespace std; // bad 

// user.cpp
#include "bad.h"

// some function that happens to be named copy
bool copy(/*... some parameters ...*/);

int main()
{
  // now overloads local ::copy and std::copy, could be ambiguous
  copy(/*...*/);
}

In my opinion, even the fact that as a reader you cannot be sure where a function comes from is bad. This is a simplistic example, but when you use using namespace in a long .cpp file it’s hard to keep track of where certain objects come from. I prefer having using-declarations instead and I also often introduce alias namespaces.

1
2
3
4
5
6
7
8
9
10
11
12
//some.h
#include <other.h>

using mcs = mynamespace::component::subcomponent;

msc::Class foo();
//some.cpp
msc::Class foo() {
  using msc::AnotherClass;
  AnotherClass bar;
  // ...
}

As such, I don’t pollute the global namespace. What you have to keep in mind is that when you introduce a using-directive into a header file at the global namespace header, you don’t just mess things up in the current scope.

If you include the header file in other files, you’ll also bring the inclusion of all those introduced symbols. If you introduce different header files with different global levels using-directives, the situation becomes even worse and the results of name lookup might depend on the order of inclusion.

To avoid all such problems, just follow SF.7 and don’t write using namespace at global scope in a header file.

Using-declaration in namespace and block scope

While the using-directive brings all the symbols of a namespace into the current scope, a using-declaration will bring only one item!

1
2
using std::string;
string foo{"bar"};

In the above example, we just demonstrated how it works. After using std::string, we can refer to std::string without mentioning the std namespace.

It’s still something not to overuse! A using-declaration may also expand an overload set. It’s less dangerous to use it at a file scope than having a using-directive at the same scope, but risks still remain.

Starting from C++20, you can also introduce scoped enumerators into a namespace of block scope!

1
2
3
4
5
6
enum class Color { red, green, blue };

class MyClass {
  using Color::red;
  Color c = red; // This is OK from C++20
};

In fact, it would also work with the old-style unscoped enum, but why would we do that?

Importing base class members with using-declaration

With using-declaration, you can introduce base class members — including constructors — into derived classes. It’s an easy way of exposing protected base class members as public in the derived class. It can be used both for functions and variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

class Base {
 protected:
  void foo() {
    std::cout << "Base::foo()n";
  }
 
 int m_i = 42; 
};


class Derived : public Base {
 public:
  using Base::foo;
  using Base::m_i;
};

int main() {
  Derived d;
  d.foo();
  std::cout << d.m_i << 'n';
}
/*
Base::foo()
42
*/

If you try to modify the above example and remove any of the two using-declarations, you’ll see the compilation failing.

If the derived class already has a member with the same name, the compilation will not. The imported symbol from the base class will be hidden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

class Base {
 protected:
  void foo() {
    std::cout << "Base::foo()n";
  }
};


class Derived : public Base {
 public:
  using Base::foo;
  
  void foo() {
    std::cout << "Derived::foo()n";
  }
};

int main() {
  Derived d;
  d.foo();
}
/*
Derived::foo()
*/

I find this technique really useful for unit testing. When you’re writing a mock by hand, you often have to expose protected member functions from the base class, from the class that you are about to mock.

One way of doing it is forwarding the call.

Hopefully, the function’s name in the mock is not changed, but I’ve seen it a couple of times. It really puts an extra burden on the maintainers when they realize that there is a better option.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ClassUnderTest {
 public:
  virtual void testMe() {
  }
  
  virtual void testMeToo() {
  }
};

class MockClassUnderTest : public ClassUnderTest {
 public:
  void testMe() override {
     ClassUnderTest::testMe(); 
  }
  
  void mockedTestMeToo() {
      ClassUnderTest::testMeToo(); 
  } 
};

Apart from tying a lot of unnecessary code, the problem above is that if the parameter list of testMe or testMeToo changes, you’ll also have to update MockClassUnderTest. You can get rid of that need by using using.

1
2
3
4
5
class MockClassUnderTest : public ClassUnderTest {
 public:
  using ClassUnderTest::testMe; 
  using ClassUnderTest::testMeToo;
};

Now we have less code and it’s more understandable what’s happening. As a bonus, even the maintenance is simplified.

Conclusion

In this article, we discussed the 4 different ways that we can use the using keyword. It’s the right way to create aliases and import base class members in derived classes. At the same time, they can be also used to introduce whole namespaces into the current scope which can be particularly dangerous. Last but not least, using can also introduce single types to the current scope which is a less dangerous option than introducing whole namespaces, still, it should be used with care.

Connect deeper

If you liked this article, please

  • hit on the like button,
  • subscribe to my newsletter
  • and let’s connect on Twitter!

Понравилась статья? Поделить с друзьями:

А вот и еще наши интересные статьи:

  • Руководство пользователя руководство по acronis disk director
  • Био дигидрокверцетин инструкция по применению цена отзывы аналоги
  • Fms 3300 руководство оператора
  • Vivacia multi a z инструкция по применению на русском языке
  • Детская кровать с бортиком своими руками пошаговая инструкция

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии