Каковы виды использования «using» в C #?


319

Пользователь kokos ответил на замечательный вопрос о скрытых возможностях C # , упомянув usingключевое слово. Можете ли вы уточнить это? Каковы виды использования using?


Это C # способ поддержки идиомы RAII: hackcraft.net/raii
Неманья Трифунович

1
Вы можете использовать для объектов, которые реализовали интерфейс IDispose. Использование вызовет метод Dispose, когда этот объект выходит из области видимости. Это гарантирует вызов Dispose, даже если произойдет какое-либо исключение. Это работает как предложение finally и выполняет Dispose.
CharithJ

Ответы:


480

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

Как и в разделе «Понимание выражения« использование »в C # (codeproject) и« Использование объектов, реализующих IDisposable (microsoft) » , компилятор C # преобразует

using (MyResource myRes = new MyResource())
{
    myRes.DoSomething();
}

в

{ // Limits scope of myRes
    MyResource myRes= new MyResource();
    try
    {
        myRes.DoSomething();
    }
    finally
    {
        // Check for a null resource.
        if (myRes != null)
            // Call the object's Dispose method.
            ((IDisposable)myRes).Dispose();
    }
}

C # 8 вводит новый синтаксис, названный « использование объявлений »:

Объявление использования - это объявление переменной, которому предшествует ключевое слово using. Он сообщает компилятору, что объявленная переменная должна быть расположена в конце области видимости.

Таким образом, эквивалентный код выше будет:

using var myRes = new MyResource();
myRes.DoSomething();

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


129
Обратите внимание, что дело не в правильности расположения объекта , а в том, своевременно ли оно было удалено. Объекты, реализующие IDisposable, которые удерживают неуправляемые ресурсы, такие как потоки и файловые дескрипторы, также реализуют финализатор, который будет гарантировать, что Dispose вызывается во время сборки мусора. Проблема в том, что GC не может происходить в течение относительно длительного времени. usingудостоверится, что он Disposeвызывается, как только вы закончите с объектом.
Джон Сондерс

1
Обратите внимание, что сгенерированный код немного отличается в случае MyRessourceструктуры. Там явно нет теста на ничтожность, но и нет бокса IDisposable. Ограниченный виртуальный вызов испускается.
Ромен Вердиер

4
Почему никто не упоминает, что использование также используется для импорта пространств имен?
Кайл Делани

3
Обратите внимание, что если вы напишете непосредственно вторую версию кода, результат не будет таким же. Если вы используете using, переменная, встроенная в нее, доступна только для чтения. Нет способа достичь этого для локальных переменных без usingоператора.
Массимилиано Краус

1
@JohnSaunders Кроме того, финализатор не гарантированно будет вызван.
Пабло Х

124

Поскольку многие люди до сих пор делают:

using (System.IO.StreamReader r = new System.IO.StreamReader(""))
using (System.IO.StreamReader r2 = new System.IO.StreamReader("")) {
   //code
}

Я думаю, что многие люди до сих пор не знают, что вы можете сделать:

using (System.IO.StreamReader r = new System.IO.StreamReader(""), r2 = new System.IO.StreamReader("")) {
   //code
}

2
Можно ли использовать несколько объектов разных типов в одном операторе using?
Агнель Курьян

12
@AgnelKurian No: «ошибка CS1044: нельзя использовать более одного типа в выражении for, using, fixed или объявление»
Дэвид Сайкс

10
Как это отвечает на вопрос?
Лиам

Я на самом деле не знал, что смогу написать два, используя государственные деятели, перед одним блоком кода (будет вкладывать их каждый раз).
kub1x

97

Вещи как это:

using (var conn = new SqlConnection("connection string"))
{
   conn.Open();

    // Execute SQL statement here on the connection you created
}

Это SqlConnectionбудет закрыто без необходимости явного вызова .Close()функции, и это произойдет, даже если выдается исключение , без необходимости try/ catch/ finally.


1
Что делать, если я использую «использование» внутри метода, и я возвращаюсь в середине использования. Есть ли проблема?
francisco_ssb

1
Здесь нет проблем. В приведенном здесь примере соединение все равно будет закрыто, даже если вы находитесь returnиз середины usingблока.
Джоэл Коухорн

30

Использование может быть использовано для вызова IDisposable. Он также может быть использован для псевдонимов типов.

using (SqlConnection cnn = new SqlConnection()) { /*code*/}
using f1 = System.Windows.Forms.Form;

21

используя, в смысле

using (var foo = new Bar())
{
  Baz();
}

На самом деле сокращение для блока try / finally. Это эквивалентно коду:

var foo = new Bar();
try
{
  Baz();
}
finally
{
  foo.Dispose();
}

Вы, конечно, заметите, что первый фрагмент гораздо более краткий, чем второй, а также что есть много вещей, которые вы можете захотеть сделать для очистки, даже если выдается исключение. Из-за этого мы создали класс, который мы называем Scope, который позволяет вам выполнять произвольный код в методе Dispose. Так, например, если у вас есть свойство IsWorking, которое вы всегда хотели установить в false после попытки выполнить операцию, вы бы сделали это следующим образом:

using (new Scope(() => IsWorking = false))
{
  IsWorking = true;
  MundaneYetDangerousWork();
}

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


12

Документация Microsoft гласит, что использование имеет двойную функцию ( https://msdn.microsoft.com/en-us/library/zhdeatwt.aspx ), как в виде директивы, так и в операторах . В качестве утверждения , как указывалось здесь в других ответах, ключевое слово в основном является синтаксическим сахаром для определения области действия для удаления объекта IDisposable . Как директива , она обычно используется для импорта пространств имен и типов. Также в качестве директивы вы можете создавать псевдонимы для пространств имен и типов, как указано в книге «C # 5.0 в двух словах: полное руководство» ( http://www.amazon.com/5-0-Nutshell-The- Definitive-реф книга / дп / B008E6I1K8Джозефом и Беном Албахари. Один пример:

namespace HelloWorld
{
    using AppFunc = Func<IDictionary<DateTime, string>, List<string>>;
    public class Startup
    {
        public static AppFunc OrderEvents() 
        {
            AppFunc appFunc = (IDictionary<DateTime, string> events) =>
            {
                if ((events != null) && (events.Count > 0))
                {
                    List<string> result = events.OrderBy(ev => ev.Key)
                        .Select(ev => ev.Value)
                        .ToList();
                    return result;
                }
                throw new ArgumentException("Event dictionary is null or empty.");
            };
            return appFunc;
        }
    }
}

Это то, что нужно принять с умом, поскольку злоупотребление этой практикой может повредить ясности кода. В DotNetPearls есть хорошее объяснение псевдонимов C #, а также упоминания о плюсах и минусах ( http://www.dotnetperls.com/using-alias ).


4
Не буду врать: я ненавижу использование usingв качестве псевдонима. Это смущает меня при чтении кода - я уже знаю, что System.Collectionsсуществует и имеет IEnumerable<T>класс. Использование псевдонима, чтобы назвать это чем-то другим, скрывает это для меня. Я вижу using FooCollection = IEnumerable<Foo>способ заставить более поздних разработчиков читать код и думать: «Что за чертовщина FooCollectionи почему для нее где-то не существует класса?» Я никогда не использую это и буду препятствовать его использованию. Но это может быть только я.
Ари Рот

1
Приложение: Я признаю, что иногда его можно использовать, как в вашем примере, где вы используете его для определения делегата. Но я бы сказал, что это относительно редко.
Ари Рот

10

В прошлом я много использовал его для работы с потоками ввода и вывода. Вы можете удобно их вкладывать, и это устраняет многие потенциальные проблемы, с которыми вы обычно сталкиваетесь (автоматически вызывая dispose). Например:

        using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
        {
            using (BufferedStream bs = new BufferedStream(fs))
            {
                using (System.IO.StreamReader sr = new StreamReader(bs))
                {
                    string output = sr.ReadToEnd();
                }
            }
        }

8

Просто добавив немного чего-то, что меня удивило, не подошло. Самая интересная особенность использования (на мой взгляд) в том, что независимо от того, как вы выйдете из блока использования, он всегда будет располагать объектом. Это включает в себя возвраты и исключения.

using (var db = new DbContext())
{
    if(db.State == State.Closed) throw new Exception("Database connection is closed.");
    return db.Something.ToList();
}

Неважно, если исключение выдается или список возвращается. Объект DbContext всегда будет расположен.


6

Еще одно замечательное применение - это создание модального диалога.

Using frm as new Form1

Form1.ShowDialog

' do stuff here

End Using

1
Вы имели в виду frm.ShowDialog?
UuDdLrLrSs

5

В заключение, когда вы используете локальную переменную типа, который реализует IDisposable, всегда без исключения используйте using1 .

Если вы используете нелокальные IDisposableпеременные, то всегда реализуйте IDisposableшаблон .

Два простых правила, без исключения 1 . Предотвращение утечек ресурсов - это настоящая боль в * ss.


1) : единственное исключение - когда вы обрабатываете исключения. Тогда может быть меньше кода для Disposeявного вызова в finallyблоке.


5

Вы можете использовать пространство имен псевдонимов в следующем примере:

using LegacyEntities = CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects;

Это называется директивой using alias, поскольку, как вы можете видеть, ее можно использовать для сокрытия длинных ссылок, если вы хотите, чтобы в вашем коде было очевидно, на что вы ссылаетесь, например:

LegacyEntities.Account

вместо того

CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects.Account

или просто

Account   // It is not obvious this is a legacy entity

4

Интересно, что вы также можете использовать шаблон использования / IDisposable для других интересных вещей (например, другой момент, которым его использует Rhino Mocks). В принципе, вы можете воспользоваться тем фактом, что компилятор всегда будет вызывать .Dispose для «используемого» объекта. Если у вас есть что-то, что должно произойти после определенной операции ... что-то с определенным началом и концом ... тогда вы можете просто создать класс IDisposable, который запускает операцию в конструкторе, а затем завершается в методе Dispose.

Это позволяет вам использовать действительно хороший синтаксис для обозначения явного начала и конца указанной операции. Так же работает и материал System.Transaction.


3

При использовании ADO.NET вы можете использовать клавиши для таких вещей, как объект подключения или объект чтения. Таким образом, когда блок кода завершится, он автоматически избавится от вашего соединения.


2
Я бы просто добавил, что блок кода даже не нужно завершать. Блок using будет распоряжаться ресурсом даже в случае необработанного исключения.
Harpo

Просто для дальнейшего разъяснения, это способ убедиться, что сборщик мусора позаботится о вашем распределении, когда вы этого хотите, вместо того, чтобы делать это, когда он хочет.
Мосвальд


3
public class ClassA:IDisposable

{
   #region IDisposable Members        
    public void Dispose()
    {            
        GC.SuppressFinalize(this);
    }
    #endregion
}

public void fn_Data()

    {
     using (ClassA ObjectName = new ClassA())
            {
                //use objectName 
            }
    }

2

Использование используется, когда у вас есть ресурс, который вы хотите утилизировать после его использования.

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

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

Пример:

using (File file = new File (parameters))
{
    *code to do stuff with the file*
}

1

Ключевое слово using определяет область действия объекта, а затем удаляет объект после завершения области действия. Например.

using (Font font2 = new Font("Arial", 10.0f))
{
    // use font2
}

Смотрите здесь статью MSDN о C # используя ключевое слово.


1

Не то чтобы это очень важно, но использование может также использоваться для изменения ресурсов на лету. Да, одноразовые, как уже упоминалось ранее, но, возможно, вам не нужны ресурсы, которые они не соответствуют другим ресурсам во время остальной части вашего исполнения. Таким образом, вы хотите избавиться от него, чтобы он не мешал в другом месте.


1

Благодаря комментариям, приведенным ниже, я немного исправлю этот пост (я не должен был использовать слова «сборка мусора» в то время, извинения):
когда вы используете, он вызовет метод Dispose () для объекта в конце области использования. Таким образом, вы можете иметь довольно много отличного кода для очистки в вашем методе Dispose ().
Пуля, которая, как мы надеемся, может получить это незаметное: Если вы реализуете IDisposable, убедитесь, что вы вызываете GC.SuppressFinalize () в вашей реализации Dispose (), так как в противном случае автоматическая сборка мусора попытается прийти и завершить ее в некоторых случаях. точка, которая, по крайней мере, будет пустой тратой ресурсов, если вы уже утилизировали () d этого.


Это имеет косвенный эффект. Поскольку вы удалили объект явно, он не требует финализации и поэтому может быть GC ранее.
Кент Бугаарт

1

Еще один пример разумного использования, при котором объект сразу же утилизируется:

using (IDataReader myReader = DataFunctions.ExecuteReader(CommandType.Text, sql.ToString(), dp.Parameters, myConnectionString)) 
{
    while (myReader.Read()) 
    {
        MyObject theObject = new MyObject();
        theObject.PublicProperty = myReader.GetString(0);
        myCollection.Add(theObject);
    }
}

1

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

using(SqlDataAdapter adapter_object = new SqlDataAdapter(sql_command_parameter))
{
   // do stuff
} // here adapter_object is disposed automatically

1

Оператор using обеспечивает удобный механизм для правильного использования IDisposable объектов. Как правило, когда вы используете объект IDisposable, вы должны объявить и создать его экземпляр в операторе using. Оператор using вызывает метод Dispose для объекта правильным образом и (при использовании его, как показано ранее) также вызывает выход объекта из области действия сразу после вызова Dispose. Внутри блока using объект доступен только для чтения и не может быть изменен или переназначен.

Это происходит от: здесь


1

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

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


1

Это также может быть использовано для создания областей, например:

class LoggerScope:IDisposable {
   static ThreadLocal<LoggerScope> threadScope = 
        new ThreadLocal<LoggerScope>();
   private LoggerScope previous;

   public static LoggerScope Current=> threadScope.Value;

   public bool WithTime{get;}

   public LoggerScope(bool withTime){
       previous = threadScope.Value;
       threadScope.Value = this;
       WithTime=withTime;
   }

   public void Dispose(){
       threadScope.Value = previous;
   }
}


class Program {
   public static void Main(params string[] args){
       new Program().Run();
   }

   public void Run(){
      log("something happend!");
      using(new LoggerScope(false)){
          log("the quick brown fox jumps over the lazy dog!");
          using(new LoggerScope(true)){
              log("nested scope!");
          }
      }
   }

   void log(string message){
      if(LoggerScope.Current!=null){
          Console.WriteLine(message);
          if(LoggerScope.Current.WithTime){
             Console.WriteLine(DateTime.Now);
          }
      }
   }

}

1

Оператор using указывает .NET освободить объект, указанный в блоке using, когда он больше не нужен. Поэтому вы должны использовать блок «using» для классов, которые требуют очистки после них, таких как System.IO Types.


1

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

  1. Как директива

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

    Пример:

    using System.IO;
  2. Как утверждение

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

    using заявлении гарантирует , что Dispose () вызывается , даже если исключение возникает при создании объектов и вызова методов, свойств и так далее. Dispose () - это метод, присутствующий в интерфейсе IDisposable, который помогает реализовать пользовательскую сборку мусора. Другими словами, если я выполняю какую-либо операцию с базой данных (Вставка, Обновление, Удаление), но каким-то образом происходит исключение, то здесь оператор using автоматически закрывает соединение. Нет необходимости вызывать метод Close () явно.

    Другим важным фактором является то, что он помогает в пуле подключений. Пул соединений в .NET помогает многократно исключить закрытие соединения с базой данных. Он отправляет объект подключения в пул для будущего использования (следующий вызов базы данных). При следующем вызове соединения с базой данных из вашего приложения пул соединений извлекает объекты, доступные в пуле. Так что это помогает улучшить производительность приложения. Поэтому, когда мы используем оператор using, контроллер автоматически отправляет объект в пул соединений, нет необходимости явно вызывать методы Close () и Dispose ().

    Вы можете сделать то же самое, что и оператор using, используя блок try-catch и явно вызывая Dispose () внутри блока finally. Но оператор using делает вызовы автоматически, чтобы сделать код чище и элегантнее. Внутри блока using объект доступен только для чтения и не может быть изменен или переназначен.

    Пример:

    string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";
    
    using (SqlConnection conn = new SqlConnection(connString))
    {
          SqlCommand cmd = conn.CreateCommand();
          cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";
          conn.Open();
          using (SqlDataReader dr = cmd.ExecuteReader())
          {
             while (dr.Read())
             Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));
          }
    }

В предыдущем коде я не закрываю никаких соединений; он закроется автоматически. usingЗаявление будет вызывать conn.Close () автоматически в связи с usingутверждением ( using (SqlConnection conn = new SqlConnection(connString)) и то же самое для объекта SqlDataReader. А также, если произойдет какое-либо исключение, оно автоматически закроет соединение.

Для получения дополнительной информации см. Использование и важность использования в C # .



-1

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

CLR преобразует ваш код в MSIL. И оператор using переводится в блок try and finally. Вот как оператор использования представлен в IL. Заявление об использовании переводится на три части: приобретение, использование и утилизация. Ресурс сначала запрашивается, затем использование включается в оператор try с предложением finally. Затем объект удаляется в предложении finally.


-3

Использование Clause используется для определения области действия определенной переменной. Например:

     Using(SqlConnection conn=new SqlConnection(ConnectionString)
            {
                Conn.Open()
            // Execute sql statements here.
           // You do not have to close the connection explicitly here as "USING" will close the connection once the object Conn becomes out of the defined scope.
            }

Это может ввести в заблуждение кого-либо, используя для утилизации предметов. Возможно, вы путаете это с блоком кода, если вы хотите ограничить область действия переменной, вы можете использовать для этого блок вложенного кода: public static void Main (params string [] args) {{// блок вложенного кода}}
luiseduardohd
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.