Jakie są zastosowania „używania” w C #?

319

Użytkownik kokos odpowiedział na cudowne Ukryte funkcje pytania C # , podając usingsłowo kluczowe. Czy możesz to rozwinąć? Jakie są zastosowania using?

ubermonkey
źródło
Jest to sposób C # na obsługę idiomu RAII: hackcraft.net/raii
Nemanja Trifunovic
1
Możesz użyć dla obiektów, które mają zaimplementowany interfejs IDispose. Użycie wywoła metodę Dispose, gdy obiekt wykracza poza zasięg. Gwarantuje wywołanie Dispose, nawet jeśli wystąpi jakiś wyjątek. Działa jak klauzula wreszcie i wykonuje polecenie Dispose.
CharithJ

Odpowiedzi:

480

Powodem tego usingstwierdzenia jest upewnienie się, że obiekt jest usuwany, gdy tylko znajdzie się poza zasięgiem, i nie wymaga jawnego kodu, aby mieć pewność, że tak się stanie.

Podobnie jak w Zrozumienie instrukcji „using” w języku C # (projekt kodu) i Korzystanie z obiektów, które implementują IDisposable (Microsoft) , kompilator C # konwertuje

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

do

{ // 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 wprowadza nową składnię o nazwie „ using declarations ”:

Deklaracja użycia to deklaracja zmiennej poprzedzona słowem kluczowym using. Mówi kompilatorowi, że deklarowana zmienna powinna zostać umieszczona na końcu otaczającego zakresu.

Więc równoważny kod powyższy byłby:

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

A gdy kontrola opuści zawarty zakres (zwykle jest to metoda, ale może to być również blok kodu), myReszostanie usunięta.

paulwhit
źródło
129
Zauważ, że niekoniecznie jest to kwestia prawidłowego usuwania obiektu , ale raczej to, czy jest on usuwany w odpowiednim czasie. Obiekty implementujące IDisposable, które utrzymują niezarządzane zasoby, takie jak strumienie i uchwyty plików, również zaimplementują finalizator, który zapewni wywołanie Dispose podczas odśmiecania. Problem polega na tym, że GC może się nie zdarzyć przez stosunkowo długi czas. usingupewnia się, że Disposejest wywoływane, gdy skończysz z obiektem.
John Saunders,
1
Pamiętaj, że generowany kod jest nieco inny, gdy MyRessourcejest strukturą. Oczywiście nie ma testu na nieważność, ale również nie ma możliwości boksowania IDisposable. Emitowane jest ograniczone połączenie wirtualne.
Romain Verdier
4
Dlaczego nikt nie wspomina, że ​​użycie służy również do importowania przestrzeni nazw?
Kyle Delaney,
3
Zauważ, że jeśli napiszesz bezpośrednio drugą wersję kodu, wynik nie będzie taki sam. Jeśli użyjesz usingzmiennej, wbudowana w nią zmienna jest tylko do odczytu. Nie ma sposobu na osiągnięcie tego dla zmiennych lokalnych bez usinginstrukcji.
Massimiliano Kraus
1
@JohnSaunders Poza tym nie można zagwarantować, że zadzwoni finalizator.
Pablo H,
124

Ponieważ wiele osób nadal:

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

Myślę, że wiele osób wciąż nie wie, że możesz to zrobić:

using (System.IO.StreamReader r = new System.IO.StreamReader(""), r2 = new System.IO.StreamReader("")) {
   //code
}
BlackTigerX
źródło
2
Czy można używać wielu obiektów różnych typów w jednej instrukcji using?
Agnel Kurian
12
@AgnelKurian No: „błąd CS1044: Nie można użyć więcej niż jednego typu w instrukcji for, using, fixed lub deklaracji”
David Sykes,
10
Jak to odpowiada na pytanie?
Liam,
Właściwie nie wiedziałem, że mogę napisać dwa przy użyciu statemen przed jednym blokiem kodu (zagnieżdżałbym je za każdym razem).
kub1x
97

Takie rzeczy:

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

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

To SqlConnectionbędzie zamknięty bez konieczności jawnie wywołać .Close()funkcję, a stanie się to nawet jeśli jest wyjątek , bez konieczności dotyczący try/ catch/ finally.

Joel Coehoorn
źródło
1
co jeśli używam „using” w metodzie i wrócę w trakcie using. Czy jest jakiś problem?
francisco_ssb
1
Nie ma problemu W tym przykładzie połączenie będzie nadal zamknięte, nawet jeśli jesteś returnw środku usingbloku.
Joel Coehoorn
30

można użyć do wywołania IDisposable. Może być również używany do typów aliasów.

using (SqlConnection cnn = new SqlConnection()) { /*code*/}
using f1 = System.Windows.Forms.Form;
MagicKat
źródło
21

za pomocą, w sensie

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

Jest tak naprawdę skrótem dla bloku try / wreszcie. Jest to równoważne z kodem:

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

Zauważysz oczywiście, że pierwszy fragment jest znacznie bardziej zwięzły niż drugi, a także, że istnieje wiele rodzajów rzeczy, które możesz zrobić jako czyszczenie, nawet jeśli zostanie zgłoszony wyjątek. Z tego powodu stworzyliśmy klasę, którą nazywamy Scope, która pozwala na wykonanie dowolnego kodu w metodzie Dispose. Na przykład, jeśli masz właściwość IsWorking, którą zawsze chciałeś ustawić na false po próbie wykonania operacji, zrobiłbyś to w ten sposób:

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

Możesz przeczytać więcej o naszym rozwiązaniu i sposobie jego uzyskania tutaj .

David Mitchell
źródło
12

Dokumentacja Microsoft stwierdza, że używanie ma podwójną funkcję ( https://msdn.microsoft.com/en-us/library/zhdeatwt.aspx ), zarówno jako dyrektywę, jak i w instrukcjach . Jako stwierdzenie , jak wskazano tutaj w innych odpowiedziach, słowem kluczowym jest cukier składniowy w celu ustalenia zakresu dysponowania obiektem IDisposable . Jako dyrektywa jest rutynowo używana do importowania przestrzeni nazw i typów. Również jako dyrektywę możesz tworzyć aliasy dla przestrzeni nazw i typów, jak wskazano w książce „C # 5.0 In a Nutshell: The Definitive Guide” ( http://www.amazon.com/5-0-Nutshell-The- Definitive-Reference-ebook / dp / B008E6I1K8), Joseph i Ben Albahari. Jeden przykład:

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;
        }
    }
}

Jest to coś, co należy mądrze zastosować, ponieważ nadużywanie tej praktyki może zaszkodzić przejrzystości kodu. Jest ładne wyjaśnienie na temat aliasów C #, wymieniając również zalety i wady, w DotNetPearls ( http://www.dotnetperls.com/using-alias ).

Aureliano Buendia
źródło
4
Nie będę kłamał: nienawidzę używania usingjako narzędzia aliasu. To dezorientuje mnie podczas czytania kodu - już wiem, że System.Collectionsistnieje i ma IEnumerable<T>klasę. Używanie aliasu, aby nazwać to czymś innym, zaciemnia to dla mnie. Widzę using FooCollection = IEnumerable<Foo>jako sposób na to, aby późniejsi programiści przeczytali kod i pomyśleli: „Co to do diabła jest FooCollectioni dlaczego nie ma gdzieś klasy?” Nigdy go nie używam i odradzałbym jego użycie. Ale to może być tylko ja.
Ari Roth,
1
Dodatek: Przyznaję, że może być to użyteczne od czasu do czasu, tak jak w twoim przykładzie, w którym używasz go do zdefiniowania delegata. Ale twierdzę, że są one stosunkowo rzadkie.
Ari Roth,
10

W przeszłości często go używałem do pracy ze strumieniami wejściowymi i wyjściowymi. Możesz je ładnie zagnieździć, a to eliminuje wiele potencjalnych problemów, na które zwykle napotykasz (automatycznie wywołując dispose). Na przykład:

        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();
                }
            }
        }
Sam Schutte
źródło
8

Dodanie czegoś, co mnie zaskoczyło, nie pojawiło się. Najbardziej interesującą cechą używania (moim zdaniem) jest to, że bez względu na to, jak wyjdziesz z używanego bloku, zawsze pozbywa się on obiektu. Obejmuje to zwroty i wyjątki.

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

Nie ma znaczenia, czy wyjątek zostanie zgłoszony, czy lista zostanie zwrócona. Obiekt DbContext zawsze będzie usuwany.

Pluc
źródło
6

Innym doskonałym zastosowaniem use jest tworzenie instancji modalnego okna dialogowego.

Using frm as new Form1

Form1.ShowDialog

' do stuff here

End Using
Lucas
źródło
1
Miałeś na myśli frm.ShowDialog?
UuDdLrLrSs,
5

Podsumowując, kiedy używasz zmiennej lokalnej typu, która implementuje IDisposable, zawsze bez wyjątku używaj using1 .

Jeśli używasz IDisposablezmiennych nielokalnych , zawsze implementuj IDisposablewzorzec .

Dwie proste zasady, bez wyjątku 1 . W przeciwnym razie zapobieganie wyciekom zasobów jest prawdziwym problemem.


1) : Jedynym wyjątkiem jest - gdy zajmujesz się wyjątkami. Może wtedy być mniej kodu do Disposejawnego wywołania w finallybloku.

Konrad Rudolph
źródło
5

Możesz skorzystać z przestrzeni nazw aliasu w następujący sposób:

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

Nazywa się to dyrektywą aliasową, jak widać, można ją wykorzystać do ukrycia od dawna odwoływanych referencji, jeśli chcesz, aby w kodzie było oczywiste, do czego się odwołujesz np.

LegacyEntities.Account

zamiast

CompanyFoo.CoreLib.x86.VBComponents.CompanyObjects.Account

lub po prostu

Account   // It is not obvious this is a legacy entity
VictorySaber
źródło
4

Co ciekawe, możesz również użyć wzorca using / IDisposable do innych interesujących rzeczy (takich jak inny sposób, w jaki używa go Rhino Mocks). Zasadniczo możesz skorzystać z faktu, że kompilator zawsze wywoła funkcję .Dispozycjonuje na „używanym” obiekcie. Jeśli masz coś, co musi się wydarzyć po określonej operacji ... coś, co ma określony początek i koniec ... to możesz po prostu stworzyć IDisposable klasę, która rozpoczyna operację w konstruktorze, a następnie kończy się w metodzie Dispose.

Pozwala to na użycie naprawdę miłej składni przy oznaczaniu wyraźnego początku i końca tej operacji. Tak działa również funkcja System.Transactions.

Joel Martinez
źródło
3

Korzystając z ADO.NET, możesz użyć klucza do takich rzeczy, jak obiekt połączenia lub obiekt czytnika. W ten sposób, gdy blok kodu zostanie ukończony, automatycznie pozbędzie się twojego połączenia.

Joseph Daigle
źródło
2
Chciałbym tylko dodać, że blok kodu nawet nie musi się kończyć. Blok używający usunie zasób nawet w przypadku nieobsługiwanego wyjątku.
harpo
Tylko w celu dalszego wyjaśnienia, jest to sposób na upewnienie się Garbage Collector dba o alokacji, gdy chcesz go, zamiast robić to kiedy on chce.
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 
            }
    }
Shiraj Momin
źródło
2

użycie jest używane, gdy masz zasób, który chcesz pozbyć się po jego użyciu.

Na przykład, jeśli alokujesz zasób pliku i potrzebujesz go użyć tylko w jednej sekcji kodu do krótkiego czytania lub pisania, użycie jest pomocne w usuwaniu zasobu pliku, gdy tylko to zrobisz.

Używany zasób musi zaimplementować IDisposable, aby działać poprawnie.

Przykład:

using (File file = new File (parameters))
{
    *code to do stuff with the file*
}
Bob Wintemberg
źródło
1

Słowo kluczowe using definiuje zakres obiektu, a następnie usuwa obiekt po zakończeniu zakresu. Na przykład.

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

Zobacz tutaj artykuł MSDN na temat C # za pomocą słowa kluczowego.

David Basarab
źródło
1

Nie jest to bardzo ważne, ale można go również używać do zmiany zasobów w locie. Tak, jednorazowe, jak wspomniano wcześniej, ale być może nie chcesz, aby zasoby były niezgodne z innymi zasobami podczas reszty egzekucji. Więc chcesz się go pozbyć, aby nie ingerował gdzie indziej.


źródło
1

Dzięki komentarzom poniżej trochę posprzątam ten post (nie powinienem wtedy używać słów „odśmiecanie”, przepraszam):
Gdy użyjesz, wywoła metodę Dispose () na obiekcie na końcu zakresu użycia. Więc możesz mieć całkiem niezły kod czyszczenia w metodzie Dispose ().
Punkt kulisty tutaj, który, mam nadzieję, może uzyskać to niezaznaczone: Jeśli zaimplementujesz IDisposable, upewnij się, że wywołujesz GC.SuppressFinalize () w implementacji Dispose (), ponieważ w przeciwnym razie nastąpi automatyczne odśmiecanie i finalizacja go w pewnym momencie punkt, który przynajmniej byłby marnotrawstwem zasobów, jeśli już go pozbyłeś () d.

Grank
źródło
Ma efekt pośredni. Ponieważ obiekt został zutylizowany w sposób jawny, nie wymaga finalizacji i dlatego można go wcześniej uzyskać.
Kent Boogaart
1

Kolejny przykład racjonalnego wykorzystania, w którym obiekt jest natychmiast usuwany:

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);
    }
}
Brendan Kendrick
źródło
1

Wszystko poza nawiasami klamrowymi jest usuwane, więc świetnie jest pozbyć się przedmiotów, jeśli ich nie używasz. Dzieje się tak, ponieważ jeśli masz obiekt SqlDataAdapter i używasz go tylko raz w cyklu życia aplikacji i wypełniasz tylko jeden zestaw danych, a nie potrzebujesz go już, możesz użyć kodu:

using(SqlDataAdapter adapter_object = new SqlDataAdapter(sql_command_parameter))
{
   // do stuff
} // here adapter_object is disposed automatically
milot
źródło
1

Instrukcja using zapewnia mechanizm wygody do prawidłowego używania obiektów IDisposable. Z reguły, gdy używasz obiektu IDisposable, powinieneś zadeklarować go i utworzyć jego instancję w instrukcji using. Instrukcja using wywołuje metodę Dispose na obiekcie we właściwy sposób i (gdy używasz go, jak pokazano wcześniej), powoduje on również, że sam obiekt wykracza poza zasięg natychmiast po wywołaniu Dispose. W używanym bloku obiekt jest tylko do odczytu i nie można go modyfikować ani ponownie przypisywać.

To pochodzi z: tutaj

Snowell
źródło
1

Dla mnie nazwa „używanie” jest nieco myląca, ponieważ może to być dyrektywa do importowania przestrzeni nazw lub instrukcji (takiej jak ta omówiona tutaj) do obsługi błędów.

Inna nazwa obsługi błędów byłaby miła, a może bardziej oczywista.

Seb
źródło
1

Można go również wykorzystać do tworzenia zakresów na przykład:

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);
          }
      }
   }

}
Siamand
źródło
1

Instrukcja using mówi .NET, aby zwolnił obiekt określony w bloku using, gdy nie jest już potrzebny. Dlatego powinieneś użyć bloku „using” dla klas, które wymagają czyszczenia po nich, takich jak Typy System.IO.

deepak samantaray
źródło
1

Istnieją dwa sposoby użycia tego usingsłowa kluczowego w języku C # w następujący sposób.

  1. Jako dyrektywa

    Zasadniczo używamy usingsłowa kluczowego, aby dodawać przestrzenie nazw w plikach kodu i klasach. Następnie udostępnia wszystkie klasy, interfejsy i klasy abstrakcyjne oraz ich metody i właściwości na bieżącej stronie.

    Przykład:

    using System.IO;
  2. Jako oświadczenie

    Jest to inny sposób użycia usingsłowa kluczowego w języku C #. Odgrywa istotną rolę w poprawie wydajności w Garbage Collection.

    Do usingrachunku zapewnia, że Dispose () jest wywoływana nawet jeśli wystąpi wyjątek podczas tworzenia obiektów i wywoływanie metod, właściwości i tak dalej. Dispose () to metoda obecna w interfejsie IDisposable, która pomaga zaimplementować niestandardowe czyszczenie pamięci. Innymi słowy, jeśli wykonuję jakieś operacje na bazie danych (Wstaw, Aktualizuj, Usuń), ale jakoś występuje wyjątek, to tutaj instrukcja using zamyka połączenie automatycznie. Nie trzeba jawnie wywoływać metody Close () połączenia.

    Innym ważnym czynnikiem jest to, że pomaga w tworzeniu puli połączeń. Pula połączeń w .NET pomaga wielokrotnie eliminować zamykanie połączenia z bazą danych. Wysyła obiekt połączenia do puli do wykorzystania w przyszłości (następne wywołanie bazy danych). Przy następnym wywołaniu połączenia z bazą danych z aplikacji pula połączeń pobiera obiekty dostępne w puli. Pomaga to poprawić wydajność aplikacji. Kiedy więc używamy instrukcji using, kontroler wysyła obiekt do puli połączeń automatycznie, nie ma potrzeby jawnego wywoływania metod Close () i Dispose ().

    Możesz zrobić to samo, co robi instrukcja using, używając bloku try-catch i wywołać jawnie funkcję Dispose () w ostatnim bloku. Ale instrukcja using wykonuje wywołania automatycznie, aby kod był czystszy i bardziej elegancki. W używanym bloku obiekt jest tylko do odczytu i nie można go modyfikować ani ponownie przypisywać.

    Przykład:

    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));
          }
    }

W poprzednim kodzie nie zamykam żadnego połączenia; zamknie się automatycznie. usingOświadczenie wezwie conn.Close () automatycznie ze względu na usingrachunku ( using (SqlConnection conn = new SqlConnection(connString)) i taka sama dla obiektu SqlDataReader. A także jeśli wystąpi jakiś wyjątek, połączenie zostanie automatycznie zamknięte.

Aby uzyskać więcej informacji, zobacz Wykorzystanie i znaczenie używania w języku C # .

Chamila Maddumage
źródło
-1

użycie jako instrukcji automatycznie wywołuje dispose na określonym obiekcie. Obiekt musi implementować interfejs IDisposable. Możliwe jest użycie kilku obiektów w jednej instrukcji, o ile są one tego samego typu.

CLR konwertuje kod na MSIL. A instrukcja using zostaje przetłumaczona na try i wreszcie blok. W ten sposób instrukcja using jest reprezentowana w IL. Instrukcja użytkowania jest tłumaczona na trzy części: nabywanie, użytkowanie i usuwanie. Najpierw pozyskiwany jest zasób, a następnie użycie jest ujęte w instrukcji try z klauzulą ​​final. Obiekt zostaje następnie umieszczony w klauzuli ostatecznie.

Vazgen Torosyan
źródło
-3

Użycie klauzuli służy do zdefiniowania zakresu dla konkretnej zmiennej. Na przykład:

     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.
            }
Riya Patil
źródło
Może to kogoś wprowadzić w błąd, używając do usuwania przedmiotów. Być może mylisz to z blokiem kodu, jeśli chcesz ograniczyć zakres zmiennej, możesz użyć do tego zagnieżdżonego bloku kodu: public static void Main (params string [] args) {{// nested code block}}
luiseduardohd