Які переваги "використання" в C #?


319

Користувач kokos відповів на чудові приховані особливості C # питання, згадуючи usingключове слово. Чи можете ви детальніше розглянути це? В чому користь using?


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

1
Ви можете використовувати для об'єктів, у яких реалізований інтерфейс IDispose. Використання буде викликати метод Dispose, коли цей об'єкт виходить за межі області. Це гарантує зателефонувати у розпорядження, навіть якщо трапляється якийсь виняток. Це працює як остаточне застереження та виконується Dispose.
CharithJ

Відповіді:


480

Причина usingтвердження полягає в тому, щоб переконатися, що об’єкт розміщений, як тільки він виходить з сфери застосування, і йому не потрібен чіткий код для того, щоб це відбулося.

Як і в розумінні оператора "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 var myRes = new MyResource();
myRes.DoSomething();

І коли контроль покине область, що містить (зазвичай це метод, але він також може бути кодовим блоком), myResбуде розміщений.


129
Зауважте, що це не обов'язково в тому, щоб об'єкт було правильно розміщено , а більше, чи він утилізований своєчасно. Об'єкти, що реалізують IDisposable, які тримаються на некерованих ресурсах, таких як потоки та ручки файлів, також реалізують фіналізатор, який забезпечить виклик утилізації під час збору сміття. Проблема полягає в тому, що 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
Чи можливо використовувати кілька об'єктів різних типів в одному операторі, що використовує?
Агнел Куріан

12
@AgnelKurian Ні: "помилка CS1044: Неможливо використовувати більше одного типу в операторі для, використання, виправлення чи декларації"
Девід Сайкс

10
Як це відповідає на питання?
Ліам

Я фактично не знав, що можу записати два, використовуючи державні елементи перед одним блоком коду (вкладав би їх кожен раз).
kub1x

97

Такі речі:

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

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

Це SqlConnectionбуде закрито, не вимагаючи явного виклику .Close()функції, і це станеться, навіть якщо буде викинуто виняток , без необхідності a try/ catch/ finally.


1
що робити, якщо я використовую метод "використання" всередині методу, і я повертаюся в середині використання. Чи є якась проблема?
francisco_ssb

1
Немає проблем. У прикладі тут з'єднання все ще буде закритим, навіть якщо ви знаходитесь returnіз середини usingблоку.
Joel Coehoorn

30

може використовуватися для виклику IDisposable. Він також може бути використаний для псевдонімів типів.

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

21

використання, в сенсі

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

Насправді це стенограма для блоку спробу / нарешті. Він еквівалентний коду:

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

Звичайно, ви зауважите, що перший фрагмент набагато більш стислий, ніж другий, а також, що є багато видів, які ви можете зробити як очищення, навіть якщо буде викинуто виняток. Через це ми створили клас, який ми називаємо Scope, який дозволяє виконувати довільний код у методі Dispose. Так, наприклад, якщо у вас був властивість під назвою IsWorking, яке ви завжди хотіли встановити на помилковий після спроби виконати операцію, ви зробите це так:

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

Ви можете прочитати більше про наше рішення та про те, як ми його отримали тут .


12

У документації Microsoft зазначається, що використання має подвійну функцію ( https://msdn.microsoft.com/en-us/library/zhdeatwt.aspx ), як в якості директиви, так і в заявах . Як твердження , як вказувалося тут в інших відповідях, ключове слово - це в основному синтаксичний цукор для визначення сфери розпорядження об'єктом, що не використовує ідентифікацію . Як директива , вона звичайно використовується для імпорту просторів імен та типів. Також в якості директиви ви можете створювати псевдоніми для просторів імен та типів, як зазначено в книзі "C # 5.0 In Nutshell: The Final Guide" ( http://www.amazon.com/5-0-Nutshell-The- Остаточний-довідник-ebook / dp / 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;
        }
    }
}

Це щось розумно слід прийняти, оскільки зловживання цією практикою може зашкодити ясності коду. Існує приємне пояснення псевдонімів C #, також згадуючи плюси і мінуси, в DotNetPearls ( http://www.dotnetperls.com/using-alias ).


4
Не брешу: я ненавиджу використання usingпсевдоніму. Це мене бентежить при читанні коду - я вже знаю, що System.Collectionsіснує і є IEnumerable<T>клас. Використання псевдоніма для того, щоб назвати його чимось іншим, заплутує його для мене. Я розглядаю using FooCollection = IEnumerable<Foo>як спосіб змусити пізніше розробників прочитати код і подумати: "Що за чорт FooCollectionі чому там десь не існує клас?" Я ніколи не користуюся ним і не перешкоджаю б його використанню. Але це я можу просто я.
Арі Рот

1
Додаток: Я визнаю, що може використовуватися для цього періодично, як у вашому прикладі, коли ви використовуєте його для визначення делегата. Але я б стверджував, що це порівняно рідко.
Арі Рот

10

У минулому я його багато використовував для роботи з вхідними та вихідними потоками. Ви можете їх добре гніздувати, і це забирає багато можливих проблем, з якими ви зазвичай стикаєтесь (автоматично викликаючи розпорядження). Наприклад:

        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;

Це називається директивою з використанням псевдоніму , як ви бачите, вона може використовуватися для приховування довгомоторних посилань, якщо ви хочете, щоб у вашому коді стало очевидним те, що ви маєте на увазі, наприклад,

LegacyEntities.Account

замість

CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects.Account

або просто

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

4

Цікаво, що ви також можете використовувати шаблон / IDisposable для інших цікавих речей (наприклад, іншого пункту, яким він користується Rhino Mocks). В основному, ви можете скористатися тим, що компілятор буде завжди викликати. Якщо у вас є щось, що повинно відбутися після певної операції ... щось, що має певний початок і кінець ..., ви можете просто зробити клас Idisposable, який запустить операцію в конструкторі, а потім закінчить методом Dispose.

Це дозволяє використовувати синтаксис справді добре, щоб позначити явний початок і кінець зазначеної операції. Так працює матеріал System.Transaction.


3

Під час використання ADO.NET ви можете використовувати ключові роботи для таких речей, як об’єкт підключення чи об'єкт зчитування. Таким чином, коли блок коду завершиться, він автоматично розпоряджатиметься вашим з'єднанням.


2
Я просто додам, що блок коду навіть не повинен завершуватися. Блок, що використовує, розпоряджатиметься ресурсом навіть у випадку незробленого винятку.
harpo

Тільки для подальшого уточнення, це спосіб переконатися , що збирач сміття піклується про ваших відрахувань , коли ви хочете його, замість того , щоб робити це , коли він хоче.
moswald


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

використання використовується, коли у вас є ресурс, який ви хочете розмістити після його використання.

Наприклад, якщо ви виділите файл-ресурс і вам потрібно використовувати його лише в одному розділі коду для невеликого читання або запису, використання корисно для видалення файлового ресурсу, як тільки ви це зробите.

Ресурс, який використовується, повинен реалізувати IDisposable для належної роботи.

Приклад:

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

1

Ключове слово, що використовує, визначає область для об'єкта, а потім розпоряджається об'єктом після завершення області. Наприклад.

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

Дивіться тут статтю MSDN на C # за допомогою ключового слова.


1

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


1

Завдяки коментарям, наведеним нижче, я трохи приберу цю публікацію (я не повинен був використовувати слова "збирання сміття" у той час, вибачте):
Коли ви користуєтесь, викликме метод об'єкта Dispose () на об'єкт в кінці області використання. Таким чином, ви можете мати дуже багато чудового коду очищення у вашому методі Dispose ().
Точка кулі, яка, сподіваємось, може отримати це відмітка: Якщо ви реалізуєте IDisposable, переконайтеся, що ви викликаєте GC.SuppressFinalize () у своїй реалізації Dispose (), оскільки в іншому випадку автоматичне збирання сміття намагатиметься зійти і завершити його на деяких точка, яка, принаймні, буде марною витратою ресурсів, якщо ви вже розпоряджаєтесь () d цим.


Він має непрямий ефект. Оскільки ви явно розпоряджалися об'єктом, він не потребує доопрацювання, а тому може бути GC'd раніше.
Кент Бугаарт

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

Оператор, що використовує, забезпечує механізм зручності для правильного використання об'єктів, що використовуються для ідентифікації. Як правило, коли ви використовуєте об'єкт, що не використовується, ви повинні оголосити його та використати в операторі, що використовує. Оператор, що використовує, викликає метод об'єкта правильним чином і (коли ви використовуєте його, як показано раніше), також призводить до того, що сам об'єкт виходить із сфери дії, як тільки викликається Dispose. У блоці, що використовує, об'єкт є лише для читання, і його не можна змінювати чи перепризначати.

Це походить від: ось


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

Використовуючи оператор повідомляє .NET звільняти об'єкт, вказаний у використанні блоку, як тільки він більше не потрібен. Тому слід використовувати блок "використання" для класів, які потребують очищення після них, як-от Типи System.IO.


1

Існує два використання usingключового слова в C # наступним чином.

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

    Зазвичай ми використовуємо using ключове слово для додавання просторів імен у файли коду та класи. Потім він робить доступними всі класи, інтерфейси та абстрактні класи та їх методи та властивості на поточній сторінці.

    Приклад:

    using System.IO;
  2. Як заява

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

    The using заяві гарантує , що Dispose () викликається , навіть якщо виключення виникає при створенні об'єктів і виклику методів, властивостей і так далі. Dispose () - метод, який присутній в інтерфейсі IDisposable, який допомагає реалізувати власну збір сміття. Іншими словами, якщо я роблю деяку операцію з базою даних (Вставити, Оновити, Видалити), але якимось чином виняток виникає, то тут використовуючий оператор закриває з'єднання автоматично. Не потрібно чітко викликати з'єднання методом Close ().

    Ще одним важливим фактором є те, що він допомагає в Об'єднанні. Пул з'єднання в .NET допомагає усунути закриття з'єднання з базою даних кілька разів. Він посилає об’єкт з'єднання в пул для подальшого використання (наступний виклик бази даних). Наступного разу, коли з вашої програми буде викликано з'єднання з базою даних, пул з'єднань вибирає об'єкти, наявні в пулі. Так це допомагає покращити продуктивність програми. Отже, коли ми використовуємо оператор, що використовує контролер, автоматично надсилає об'єкт до пулу з'єднання, немає необхідності чітко викликати методи Close () та Dispose ().

    Ви можете зробити те ж саме, що робить оператор using, використовуючи блок try-catch і викличте Dispose () всередині остаточного блоку явно. Але оператор використання робить дзвінки автоматично, щоб зробити код більш чистим та елегантним. У блоці, що використовує, об'єкт є лише для читання, і його не можна змінювати чи перепризначати.

    Приклад:

    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 переводиться на спробу і остаточно блокує. Ось так представлений використовуваний оператор в IL. Використовуючий оператор перекладається на три частини: придбання, використання та розпорядження. Спочатку отримується ресурс, потім використання вкладається у спробу оператора з остаточно застереженням. Потім об'єкт розміщується в остаточному пункті.


-3

Використовуючи пункт, використовується для визначення області для конкретної змінної. Наприклад:

     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 (параметр string [] args) {{// вкладений блок коду}}
luiseduardohd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.