Skip to main content

C# vs Java. Using

· 5 min read
Andrey Ganyushkin

Отталкнемся от директивы/оператора using в C# и посмотрим что там есть в Java.

Директива / Оператор using

Импорт типов

using позволяет использовать типы описанные в других пространствах имен без указания fully-qualified-name

Например:

using MyApplication.MyPackage;
// ...
var myClass = new MyClass();

тут мы можем использовать myClass не указывая молный путь к классу MyApplication.MyPackage.MyClass

Псевдонимы

Псевдонимы можно создавать для пространства имен или конкретных типов как показанно ниже

using MyPackageAlias = MyApplication.MyPackage;

// or for type alias
using ClassToUse = MyApplication.MyPackage.MyClass;

С помощью using можно сделать псевдоним на параметризированный generic:

using IntListAlias = List<int>;
//...
var list = new IntListAlias();
list.Add(42);

Глобальный импорт через using

  • Действие using распространяется на текущий файл.
  • using может быть объявлен в начале файла или в начале пространства имен.

С помощью global, сделав только одно объявление, можно импортировать содержимое пространства имен во всех файлах проекта.

global using MyApplication.MyPackage;

Содержимое покате MyPackage будет импортировано во все файлы проекта.

Глобальный импорт через файл проекта

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

...
<Using Include="MyApplication.MyPackage" />
...

Импорт статических членов классов

В случае когда наш тип MyClass содержит статические члены или вложенные типы - мы можем импортировать только их и использовать их без указания типа MyClass.

Например:

// for class
namespace MyApplication.MyPackage {
class MyClass {
static int Add(int a, int b) {
return a + b;
}
}
}

// we can use it as
using static MyApplication.MyPackage.MyClass;
// ...
var sum = Add(1, 2);

Аналогично для вложенных типов.

Еще пример из документации:

using static System.Console;
using static System.Math;

class Program
{
static void Main()
{
WriteLine(Sqrt(3*3 + 4*4));
}
}

Wildcard Imports

Если мы пишем using MyApplication.MyPackage; - это значит что мы делаем доступными все типы из пространства имен MyApplication.MyPackage. Такое поведение вполне точно описывает что мы подразумеваем под wildcard imports когда в Java пишем import MyApplication.MyPackage.*;. Получается, что wildcard imports для using в C# - это поведение по умолчанию.

Важно отметить что в C# мы не можем импортировать конкретный тип c using name

using MyApplication.MyPackage.MyClass; // incorrect

такое возможно только с использованием алиаса

using MyClassAlias = MyApplication.MyPackage.MyClass; // correct

// or with same name
using MyClass = MyApplication.MyPackage.MyClass; // correct

но такой подход уже выглядит как будто мы делаем что-то не правильно.

Инструкция using и IDisposable

Как это работает?

using в C# может использоваться как оператор для автоматического закрытия объектов имплементирующих IDisposable.

Пример с StreamReader

string txt = String.Empty;
using (StreamReader sr = new StreamReader(filename))
{
txt = sr.ReadToEnd();
}

В этом примере при входе в блок using будет создан поток StreamReader, а при выходе этот поток будет закрыт посредствам вызова метода Dispose() у StreamReader.

  • using похож на try-with-resources в Java.

  • await using используется для IAsyncDisposable

  • в using можно передать объект сласса имплементирующего IDisposable

var reader = new StringReader(manyLines);
using (reader) { ... }
  • в using можно объединить несколько объявлений одного типа
using (StringReader left = new StringReader(numbers),
right = new StringReader(letters)) { ... }

IDisposable Interface

Интерфейс для реализации освобождения неуправляемых ресурсов.

Стоит быть аккуратных, возможны проблемы если метод Dispose не будет вызван и ресурсы не будут освобождены. Как решение этой проблемы документация предлагает следующее:

Because the IDisposable.Dispose implementation is called by the consumer of a type when the resources owned by an instance are no longer needed, you should either wrap the managed object in a SafeHandle (the recommended alternative), or you should override Object.Finalize to free unmanaged resources in the event that the consumer forgets to call Dispose.

IAsyncDisposable Interface

Интерфейс для реализации асинхронного освобождения неуправляемых ресурсов.

А что там в Java?

Все немного похоже и немного другое чем в C#. Аналог для using директивы - это import. А для using оператора - это try-with-resources.

Рассмотрим далее подробнее.

import как аналог using

Импортировать тип из другого пакета можно так

import MyApplication.MyPackage.MyClass;

// ...
var x = new MyClass();

тут стоит заметить что мы импортируем не все содержимое пакета, а конкретный тип.

Импорт статических членов классов

В Java мы можем импортировать статический член используя import static

import static MyApplication.MyPackage.MyClass.staticMethid;

это даст нам возможность использовать функцию staticMethid в коде текущего файла без указания имен пакета и класса.

Пример из документации

import static java.lang.Math.cos;
import static java.lang.Math.PI;

double r = cos(PI * theta);

Глобальный импорт как c global using

Нет.

Wildcard Imports

Такой подход в Java будем считать не самым лучшим выбором при написании кода, так как используя это:

import MyApplication.MyPackage.*;

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

Поэтому, в Java подход по умолчанию - это импорт только нужного типа вот так:

import MyApplication.MyPackage.MyClass;

try-with-resources

Как это работает?

В Java try (если он with-resources) умеет "закрывать" объекты. Совсем как using в C#.

Мы можем использовать объект, реализующий интерфейс AutoCloseable, как приведено ниже. try в этом случае, помимо своей обычной работы по контролю за исключениями, вызовет метод close() для освобождения ресурсов в конце работы своего блока.

try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
writer.println("Hello World");
}

AutoCloseable Interface

Все как в C# для IDisposable только в Java и для AutoCloseable и название метода другое.

An object that may hold resources (such as file or socket handles) until it is closed. The close() method of an AutoCloseable object is called automatically when exiting a try-with-resources block for which the object has been declared in the resource specification header.

Conclusion

Мне показалось что в C# с using больше способов выстрелить себе в ногу. Возможно это из-за:

  • wildcard imports для using в C# - это поведение по умолчанию
  • global using