İstisnai güvenlik için "kapsamlı davranış" elde etmenin bir yolu olarak IDisposable kullanmak ve "kullanmak" kötüye kullanım mıdır?


112

C ++ 'da sıklıkla kullandığım bir şey, bir sınıfın Abaşka bir sınıf için bir durum girişi ve çıkış koşulunu kurucu ve yıkıcı Baracılığıyla işlemesine izin Avermekti, bu kapsamdaki bir şey bir istisna atarsa, B'nin bilinen bir duruma sahip olacağından emin olmak için kapsamdan çıkıldı. Kısaltmaya göre bu saf RAII değil, ancak yine de yerleşik bir kalıp.

C # 'da sık sık yapmak istiyorum

class FrobbleManager
{
    ...

    private void FiddleTheFrobble()
    {
        this.Frobble.Unlock();
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
        this.Frobble.Lock();
    }
}

Hangisinin böyle yapılması gerekiyor

private void FiddleTheFrobble()
{
    this.Frobble.Unlock();

    try
    {            
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
    }
    finally
    {
        this.Frobble.Lock();
    }
}

Döndüğünde Frobbledevleti garanti etmek istersem FiddleTheFrobble. Kod ile daha güzel olurdu

private void FiddleTheFrobble()
{
    using (var janitor = new FrobbleJanitor(this.Frobble))
    {            
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
    }
}

nerede FrobbleJanitorgörünüyor kabaca gibi

class FrobbleJanitor : IDisposable
{
    private Frobble frobble;

    public FrobbleJanitor(Frobble frobble)
    {
        this.frobble = frobble;
        this.frobble.Unlock();
    }

    public void Dispose()
    {
        this.frobble.Lock();
    }
}

Ben de böyle yapmak istiyorum. Ne yana Şimdi gerçeklik yetişene istediğiniz kullanımına ihtiyaç olduğu FrobbleJanitorkullanılır ile using . Bunu bir kod inceleme sorunu olarak düşünebilirim, ancak bir şey beni rahatsız ediyor.

Soru: Would yukarıda küfürlü kullanımı gibi düşünülebilir usingve IDisposable?


23
Yalnızca sınıf ve yöntem adları için +1 :-). Eh, adil olmaya ve gerçek bir soru.
Joey

2
@Johannes: Evet, nedenini anlayabiliyorum - çoğu insan sadece kemanlarını becermiyor, ama buna karşı çıkmam gerekiyor. ;-) Ve bu arada, isim benzerliğiniz için +1.
Johann Gerell

Bu örnekte aynı sonucu elde etmek için belki kilit (bu) {..} kapsamını kullanabilirsiniz?
oɔɯǝɹ

1
@ oɔɯǝɹ: Farklı iş parçacıklarından bir şeye aynı anda erişimi önlemek için lock () kullanırsınız. Tarif ettiğim senaryo ile hiçbir ilgisi yok.
Johann Gerell

1
Bir sonraki C # :) sürümünde Sonlandırıcı olmadan kapsam dahilinde
Jon Raynor

Yanıtlar:


33

İlle de öyle düşünmüyorum. Teknik olarak IDisposable , yönetilmeyen kaynaklara sahip şeyler için kullanılmak üzere tasarlanmıştır, ancak daha sonra kullanma yönergesi try .. finally { dispose },.

Bir pürist, "evet - taciz edicidir" ve saf anlamıyla öyledir; ama çoğumuz pürist bir bakış açısıyla değil, yarı sanatsal bir bakış açısıyla kodluyoruz. "Kullanarak" yapıyı bu şekilde kullanmak, bence gerçekten oldukça sanatsal.

Muhtemelen IDisposable'ın üstüne başka bir arayüz yapıştırarak onu biraz daha uzağa itmelisiniz ve diğer geliştiricilere bu arayüzün neden IDisposable olduğunu açıklamalısınız.

Bunu yapmanın pek çok başka alternatifi var ama nihayetinde bu kadar düzgün olacağını düşünemiyorum, o yüzden devam et!


2
"Kullanmanın" sanatsal bir kullanımı olduğuna da inanıyorum. Samual Jack'in Eric Gunnerson'dan gelen bir yoruma bağlanan gönderisinin bu "kullanma" uygulamasını doğruladığını düşünüyorum. Bu konuda hem Andras hem de Eric tarafından yapılan mükemmel noktaları görebiliyorum.
IAbstract

1
Eric Gunnerson ile bir süre önce bunun hakkında konuştum ve ona göre C # tasarım ekibinin amacı, kullanımın kapsamları ifade etmekti. Aslında, kilit ifadesinden önce kullanmayı tasarladıklarını, bir kilit ifadesi bile olmayabileceğini varsaydı. Monitör çağrısı veya başka bir şey ile bir kullanım bloğu olurdu. GÜNCELLEME: Bir sonraki cevabın, aynı zamanda dil ekibinden Eric Lippert olduğunu fark ettim. ;) Belki de C # ekibinin kendisi bu konuda tam bir fikir birliği içinde değil? Gunnerson ile tartışmamın bağlamı TimedLock sınıfıydı: bit.ly/lKugIO
Haacked

2
@Haacked - Eğlenceli değil mi; yorumunuz benim düşüncemi tamamen kanıtlıyor; takımdaki bir adamla konuştuğunu ve onun için olduğunu; sonra aynı takımdan Eric Lippert'e işaret ederek aynı fikirde değil. Benim açımdan; C #, bir arabirimden yararlanan ve aynı zamanda pek çok senaryoda çalışan hoş görünümlü bir kod kalıbı sağlayan bir anahtar sözcük sağlar. Kötüye kullanmamamız gerekiyorsa, C # bunu zorlamanın başka bir yolunu bulmalı. Her iki durumda da, tasarımcıların muhtemelen ördeklerini burada arka arkaya almaları gerekiyor! Bu arada: using(Html.BeginForm())efendim? yapsam da sorun değil efendim! :)
Andras Zoltan

71

Bunu, kullanım ifadesinin kötüye kullanılması olarak görüyorum. Bu pozisyonda azınlıkta olduğumun farkındayım.

Bunu üç nedenden dolayı kötüye kullanma olarak görüyorum.

Birincisi, "kullanma" nın bir kaynağı kullanmak ve onunla işiniz bittiğinde onu atmak için kullanılmasını bekliyorum . Program durumunu değiştirmek bir kaynak kullanmamaktır ve onu geri değiştirmek hiçbir şeyi elden çıkarmaz. Bu nedenle, durumu değiştirmek ve geri yüklemek için "kullanmak" kötüye kullanımdır; kod sıradan okuyucular için yanıltıcıdır.

İkincisi, "kullanma" nın gereklilikten değil, nezaketten kullanılmasını bekliyorum . İşiniz bittiğinde bir dosyayı imha etmek için "kullanma" seçeneğini kullanmanızın nedeni, bunun gerekli olması değil, kibar olmasıdır - başka biri o dosyayı kullanmak için bekliyor olabilir, bu yüzden "bitti" şimdi "ahlaki olarak yapılacak doğru şeydir. Kullanılmış kaynağın daha uzun süre tutulması ve daha sonra atılması için bir "kullanımı" yeniden düzenleyebilmeyi ve bunu yapmanın tek etkisinin diğer süreçleri biraz rahatsız etmek olmasını bekliyorum . Program durumu üzerinde anlamsal etkisi olan "kullanan" bir blok zorunluluk değil, kolaylık ve nezaket için varmış gibi görünen bir yapıda program durumunun önemli, gerekli bir mutasyonunu gizlediği için kötüye kullanılır.

Üçüncüsü, programınızın eylemleri durumuna göre belirlenir; Devletin dikkatli manipülasyonuna duyulan ihtiyaç tam da bu konuşmayı ilk etapta yapmamızın nedenidir. Orijinal programınızı nasıl analiz edebileceğimizi düşünelim.

Bunu ofisimdeki bir kod incelemesine götürseydiniz, soracağım ilk soru "bir istisna atılırsa frobble'ı kilitlemek gerçekten doğru mu?" Programınızdan, bu şeyin ne olursa olsun gevezeliği agresif bir şekilde yeniden kilitlediği apaçık ortadadır. Bu doğru mu? Bir istisna atıldı. Program bilinmeyen bir durumda. Foo, Fiddle veya Bar'ın atıp atmadığını, neden fırlattıklarını veya temizlenmemiş başka bir duruma hangi mutasyonları gerçekleştirdiklerini bilmiyoruz. Beni o korkunç durumda yeniden kilitlemenin her zaman yapılacak doğru şey olduğuna ikna edebilir misin ?

Belki öyledir, belki de değildir. Demek istediğim, kod ilk yazıldığı haliyle , kod gözden geçiren kişinin soruyu sorması gerektiğini biliyor . "Kullanarak" kullanan kodla, soruyu sormam gerektiğini bilmiyorum; Bunun, yapılmazsa o zaman blok ayırır kaynak, biraz kullanır bunu ve bunun kibarca hizmetinde "kullanılarak" farz kapanış ayracı "kullanılarak" blok içinde mutasyon keyfi pek istisnai cirumstance içinde benim program devlet program durumu tutarlılık koşulları ihlal edildi.

Anlamsal bir etkiye sahip olmak için "kullanma" bloğunun kullanılması, bu program parçasını yapar:

}

son derece anlamlı. Bu tek yakın ayraca baktığımda, "bu küme ayracın, programımın küresel durumu üzerinde geniş kapsamlı etkileri olan yan etkileri olduğunu" hemen düşünmüyorum. Ama bu şekilde "kullanmayı" kötüye kullandığınızda, aniden kötüye gidiyor.

Orijinal kodunuzu görürsem soracağım ikinci şey, "Kilit Açma işleminden sonra ancak deneme girilmeden önce bir istisna atılırsa ne olur?" Optimize edilmemiş bir derleme çalıştırıyorsanız, derleyici denemeden önce işlemsiz bir talimat eklemiş olabilir ve işlemsiz durumda bir iş parçacığı iptal istisnasının gerçekleşmesi mümkündür. Bu nadirdir, ancak gerçek hayatta özellikle web sunucularında olur. Bu durumda, kilit açma gerçekleşir ancak kilit asla olmaz, çünkü istisna denemeden önce fırlatılır. Bu kodun bu soruna karşı savunmasız olması tamamen mümkündür ve aslında yazılmalıdır

bool needsLock = false;
try
{
    // must be carefully written so that needsLock is set
    // if and only if the unlock happened:

    this.Frobble.AtomicUnlock(ref needsLock);
    blah blah blah
}
finally
{
    if (needsLock) this.Frobble.Lock();
}

Yine olabilir, belki olmaz, ama soruyu sormayı biliyorum . "Kullanan" sürümle, aynı soruna karşı hassastır: Frobble kilitlendikten sonra, ancak kullanımla ilişkili deneme korumalı bölge girilmeden önce bir iş parçacığı iptal istisnası atılabilir. Ancak "kullanma" sürümüyle, bunun "ne olmuş yani?" Olduğunu varsayıyorum. durum. Bunun olması talihsiz bir durumdur, ancak "kullanma" nın hayati önem taşıyan program durumunu değiştirmek için değil, yalnızca kibar olmak için olduğunu varsayıyorum. Sanırım, bazı korkunç iş parçacığı iptali istisnası tam olarak yanlış zamanda meydana gelirse, o zaman, o zaman, çöp toplayıcının sonunda sonlandırıcıyı çalıştırarak bu kaynağı temizleyeceğini varsayıyorum.


18
Soruyu farklı cevaplayacak olsam da, bunun iyi tartışılmış bir yazı olduğunu düşünüyorum. Ancak, usingdoğruluk sorunundan çok nezaket meselesi olan iddianıza itiraz ediyorum . Kaynak sızıntılarının doğruluk sorunları olduğuna inanıyorum, ancak çoğu zaman yüksek öncelikli hatalar olmamak için yeterince küçük olmalarına rağmen. Bu nedenle, usingblokların program durumu üzerinde zaten anlamsal etkisi vardır ve önledikleri şey, diğer süreçler için sadece "rahatsızlık" değil, muhtemelen bozuk bir ortamdır. Bu, sorunun ima ettiği farklı türden anlamsal etkilerin de uygun olduğu anlamına gelmez.
kvb

13
Kaynak sızıntıları doğruluk sorunlarıdır, evet. Ancak "kullanma" nın kullanılmaması kaynağı sızdırmaz; Sonlandırıcı çalıştığında kaynak eninde sonunda temizlenecektir. Temizleme, istediğiniz kadar agresif ve zamanında olmayabilir, ancak gerçekleşecektir.
Eric Lippert

7
Harika gönderi, işte benim iki sentim :) usingC # 'da fakir bir adamın bakış açısına yönelik dokumacısı olduğu için "kötüye kullanımın" çoğundan şüpheleniyorum . Geliştiricinin gerçekten istediği şey, bazı ortak kodların bir bloğun sonunda bu kodu kopyalamak zorunda kalmadan çalışacağını garanti etmenin bir yoludur. C # bugün AOP'yi desteklemiyor (MarshalByRefObject & PostSharp buna dayanmıyor). Bununla birlikte, derleyicinin using ifadesini dönüştürmesi, deterministik kaynak kullanımının anlambilimini yeniden amaçlayarak basit AOP elde etmeyi mümkün kılar. İyi bir uygulama olmasa da bazen geçmek çekici
gelebilir

11
Genel olarak sizin konumunuza katılıyorum, ancak yeniden amaçlamanın faydasının usingvazgeçilemeyecek kadar çekici olduğu bazı durumlar vardır . Aklıma gelen en iyi örnek, kod zamanlamalarını izlemek ve raporlamaktır: using( TimingTrace.Start("MyMethod") ) { /* code */ } Yine, bu AOP - Start()blok başlamadan önceki başlangıç ​​zamanını Dispose()yakalar, bitiş zamanını yakalar ve etkinliği günlüğe kaydeder. Program durumunu değiştirmez ve istisnalara karşı agnostiktir. Aynı zamanda çekici, çünkü oldukça fazla su tesisatından kaçınıyor ve bir kalıp olarak sıkça kullanılması kafa karıştırıcı anlambilimini hafifletiyor.
LBushkin

6
Komik, her zaman RAII'yi desteklemenin bir yolu olarak kullanmayı düşünmüşümdür. Kilidin bir tür kaynak olduğunu düşünmek bana mantıklı geliyor.
Brian

27

Temiz, kapsamlı bir kod istiyorsanız, ayrıca lambdas, á la

myFribble.SafeExecute(() =>
    {
        myFribble.DangerDanger();
        myFribble.LiveOnTheEdge();
    });

burada .SafeExecute(Action fribbleAction)yöntem, sarar try- catch- finallyblok.


Bu örnekte, giriş durumunu kaçırdınız. Dahası, denemeyi / nihayet tamamladığınızdan emin olun, ancak sonunda hiçbir şeyi çağırmazsınız, bu nedenle lambda içindeki bir şey fırlatırsa hangi durumda olduğunuzu bilemezsiniz.
Johann Gerell

1
Ah! Tamam, sanırım bu çağıran ve sarmalanmış denemede / nihayetinde SafeExecutebir Fribbleyöntem olduğunu söylüyorsunuz . O zaman özür dilerim. Hemen genel bir uzatma yöntemi olarak gördüm ve bu nedenle giriş ve çıkış durumunun eksikliğinden söz ettim . Benim hatam. Unlock()Lock()SafeExecute
Johann Gerell

1
Bu yaklaşıma çok dikkat edin. Yerel halkın yakalanması durumunda, bazı tehlikeli ince, istenmeyen ömür uzatmaları olabilir!
jason

4
Yuk. Neden sonunda dene / yakala / sakla? Kodunuzu başkaları için okumak üzere gizler.
Frank Schwieterman

2
@Yukky Frank: Bir şeyi "gizlemek" benim fikrim değildi, soruyu soran kişinin isteğiydi . :-P ... Dedi ki, istek sonunda "Kendini Tekrar Etme" meselesi. Bir şeyi temiz bir şekilde almak / bırakmak için aynı standart "çerçeve" yi gerektiren birçok yönteme sahip olabilirsiniz ve bunu arayana empoze etmek istemezsiniz (kapsüllemeyi düşünün). Ayrıca .SafeExecute (...) gibi yöntemler, yaptıklarını yeterince iyi anlatmak için daha açık bir şekilde adlandırılabilir.
herzmeister

25

C # dil tasarım ekibinde yer alan Eric Gunnerson, hemen hemen aynı soruya şu cevabı verdi :

Doug soruyor:

re: Zaman aşımına sahip bir kilit ifadesi ...

Bu numarayı daha önce çeşitli yöntemlerde ortak kalıplarla uğraşmak için yaptım . Genellikle edinimi kilitler , ancak başkaları da vardır . Sorun şu ki, nesne " kapsamın sonunda geri arama " kadar gerçekten tek kullanımlık olmadığından, her zaman bir hack gibi hissettiriyor .

Doug

Using ifadesine karar verdiğimizde, tam olarak bu senaryo için kullanılabilmesi için nesneleri atmaya daha özel bir şeyden ziyade "kullanarak" olarak adlandırmaya karar verdik.


4
Bu alıntı, "tam olarak bu senaryo" derken neyi kastettiğini söylemeli, çünkü gelecekteki bu sorudan bahsettiğinden şüpheliyim.
Frank Schwieterman

4
@Frank Schwieterman: Alıntıyı tamamladım. Görünüşe göre, C # ekibinden insanlar yaptığını düşünüyorum usingözellik oldu değil kaynak bertaraf sınırlı olmaya.
paercebal

11

Kaygan bir yokuş. IDisposable'ın bir sonlandırıcı tarafından desteklenen bir sözleşmesi var. Senin durumunda bir sonuçlandırıcı işe yaramaz. Müşteriyi using ifadesini kullanmaya zorlayamazsınız, yalnızca onu kullanmaya teşvik edin. Bunu aşağıdaki gibi bir yöntemle zorlayabilirsiniz:

void UseMeUnlocked(Action callback) {
  Unlock();
  try {
    callback();
  }
  finally {
    Lock();
  }
}

Ama bu, lamdalar olmadan biraz tuhaf olma eğilimindedir. Bununla birlikte, senin yaptığın gibi IDisposable kullandım.

Ancak yazınızda bunu tehlikeli bir şekilde anti-desene yaklaştıran bir ayrıntı var. Bu yöntemlerin bir istisna oluşturabileceğinden bahsettiniz. Bu, arayan kişinin görmezden gelebileceği bir şey değildir. Bununla ilgili üç şey yapabilir:

  • Hiçbir şey yapmayın, istisna kurtarılamaz. Normal durum. Unlock'u aramak önemli değil.
  • İstisnayı yakalayın ve işleyin
  • Durumu kodunda geri yükleyin ve istisnanın çağrı zincirini geçmesine izin verin.

Son ikisi, arayanın açıkça bir try bloğu yazmasını gerektirir. Şimdi using ifadesi önümüze çıkıyor. Bir müşteriyi komaya sokarak onu sınıfınızın devletle ilgilendiğine ve ek bir işin yapılmasına gerek olmadığına inandırabilir. Bu neredeyse hiçbir zaman doğru değildir.


Zorunlu dava yukarıda "herzmeister der welten" tarafından verildi, sanırım - OP örneği beğenmemiş gibi görünse bile.
Benjamin Podszun

Evet, onu SafeExecutegenel bir uzatma yöntemi olarak yorumladım . Şimdi görüyorum ki, büyük olasılıkla Fribbleçağıran Unlock()ve Lock()sarmalanmış denemede / sonunda bir yöntem olarak kastedildi . Benim hatam.
Johann Gerell

Bir istisnayı göz ardı etmek, kurtarılamayacağı anlamına gelmez. Bu, yığında yukarıda ele alınacağı anlamına gelir. Örneğin, bir iş parçacığından düzgün bir şekilde çıkmak için istisnalar kullanılabilir. Bu durumda, kilitli kilitler de dahil olmak üzere kaynaklarınızı doğru bir şekilde serbest bırakmanız gerekir.
paercebal

7

Gerçek bir dünya örneği, ASP.net MVC'nin BeginForm'udur. Temel olarak şunları yazabilirsiniz:

Html.BeginForm(...);
Html.TextBox(...);
Html.EndForm();

veya

using(Html.BeginForm(...)){
    Html.TextBox(...);
}

Html.EndForm Dispose çağırır ve Dispose yalnızca </form>etiketi çıkarır . Bununla ilgili iyi olan şey, {} parantezlerinin görünür bir "kapsam" oluşturmasıdır, bu da formun içinde neyin olup olmadığını görmeyi kolaylaştırır.

Aşırı kullanmazdım, ama özünde IDisposable, "İşiniz bittiğinde bu işlevi çağırmanız GEREKİR" demenin bir yolu. MvcForm bunu formun kapalı olduğundan emin olmak için kullanır, Akış bunu akışın kapalı olduğundan emin olmak için kullanır, nesnenin kilidinin açıldığından emin olmak için bunu kullanabilirsiniz.

Şahsen bunu yalnızca aşağıdaki iki kural doğruysa kullanırdım, ancak bunlar benim tarafımdan keyfi olarak belirlenir:

  • Dispose, her zaman çalışması gereken bir işlev olmalıdır, bu nedenle Null-Checks dışında herhangi bir koşul olmamalıdır
  • Dispose () işleminden sonra, nesne yeniden kullanılabilir olmamalıdır. Yeniden kullanılabilir bir nesne isteseydim, elden çıkarmak yerine ona açma / kapama yöntemleri vermeyi tercih ederim. Bu yüzden, atılmış bir nesneyi kullanmaya çalışırken InvalidOperationException oluşturuyorum.

Sonunda, her şey beklentilerle ilgili. Bir nesne IDisposable uygularsa, biraz temizleme yapması gerektiğini varsayıyorum, bu yüzden onu çağırıyorum. Sanırım genellikle bir "Kapat" işlevine sahip olmaktan daha iyidir.

Bununla birlikte, bu satırı sevmiyorum:

this.Frobble.Fiddle();

Frobble Janitor artık Frobble'ın "sahibi" olduğundan, bunun yerine Janitor'daki Frobble'a Fiddle demek daha iyi olmaz mıydı acaba?


"Bu satırı beğenmedim" in hakkında - Aslında soruyu formüle ederken bu şekilde yapmayı kısaca düşündüm, ama sorumun anlamını karıştırmak istemedim. Ama sana biraz katılıyorum. Türü. :)
Johann Gerell

1
Microsoft'un kendisinden bir örnek sağladığı için oy verildi. Bahsettiğiniz durumda atılacak belirli bir istisna olduğunu unutmayın: ObjectDisposedException.
Antitoon

4

Kod tabanımızda bu kalıbı bolca kullanıyoruz ve daha önce her yerde görmüştüm - eminim burada da tartışılmış olmalı. Genel olarak bunu yapmanın neyin yanlış olduğunu görmüyorum, yararlı bir model sağlıyor ve gerçek bir zarara yol açmıyor.


4

Şuna inanıyorum: Buradaki çoğuna bunun kırılgan ama yararlı olduğu konusunda hemfikirim. Sizi, yapmak istediğiniz gibi bir şey yapan System.Transaction.TransactionScope sınıfına yönlendirmek istiyorum.

Genelde sözdizimini seviyorum, gerçek etten çok fazla dağınıklığı ortadan kaldırıyor. Lütfen yardımcı sınıfa iyi bir isim vermeyi düşünün - belki ... Yukarıdaki örnekte olduğu gibi Kapsam. İsim, bir kod parçasını kapsadığını belirtmelidir. * Kapsam, * Blok veya benzeri bir şey yapmalıdır.


1
"Kapıcı", Andrei Alexandrescu'nun dürbün korumaları hakkında daha ScopeGuard Loki'ye gitmeden önce yazdığı eski bir C ++ makalesinden geliyor.
Johann Gerell

@Benjamin: TransactionScope sınıfını beğendim, daha önce duymamıştım. Belki de dahil olmadığı .Net CF ülkesindeyim ... :-(
Johann Gerell

4

Not: Bakış açım muhtemelen C ++ geçmişime göre önyargılıdır, bu nedenle cevabımın değeri bu olası önyargıya karşı değerlendirilmelidir ...

C # Dil Spesifikasyonu Ne Diyor?

C # Dil Belirtiminden Alıntı :

8.13 using ifadesi

[...]

Bir kaynak bir sınıf veya yapı olup bertaraf adlı tek parametresiz yöntemi içerir uygular System.IDisposable, bu. Bir kaynağı kullanan kod, kaynağa artık ihtiyaç olmadığını belirtmek için Dispose çağırabilir. Dispose çağrılmazsa, çöp toplamanın bir sonucu olarak sonunda otomatik imha gerçekleşir.

Bir kaynak kullanıyor Kod elbette kod ile başlıyor usinganahtar kelime ve kapsamı ekli kadar devamusing .

Sanırım bu sorun değil çünkü Kilit bir kaynak.

Belki de anahtar kelime usingkötü seçilmiştir. Belki de çağrılmalıydıscoped .

O zaman neredeyse her şeyi bir kaynak olarak düşünebiliriz. Bir dosya tanıtıcısı. Bir ağ bağlantısı ... Bir ileti dizisi mi?

Bir ileti dizisi ???

usingAnahtar kelimeyi kullanmak (veya kötüye kullanmak) ?

O olurdu parlak kullanın (ab) usingemin parçacığının iş kapsamını çıkmadan önce sona yapmaya anahtar kelimeyi?

Herb Sutter , bir iş parçacığının çalışmasının bitmesini beklemek için IDispose modelinin ilginç bir kullanımını sunduğu için parlak olduğunu düşünüyor gibi görünüyor :

http://www.drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=225700095

İşte makaleden kopyalanıp yapıştırılan kod:

// C# example
using( Active a = new Active() ) {    // creates private thread
       
       a.SomeWork();                  // enqueues work
       
       a.MoreWork();                   // enqueues work
       
} // waits for work to complete and joins with private thread

Etkin nesnenin C # kodu sağlanmadığında, C #, yıkıcıda C ++ sürümü bulunan kod için IDispose modelini kullanır. Ve C ++ sürümüne bakarak, makalenin bu diğer özünde gösterildiği gibi, çıkmadan önce iç iş parçacığının bitmesini bekleyen bir yıkıcı görüyoruz:

~Active() {
    // etc.
    thd->join();
 }

Yani, Herb söz konusu olduğunda, parlak .


3

Sorunuzun cevabının hayır olduğuna inanıyorum, bu kötüye kullanım değil IDisposable.

IDisposableArayüzü anlamamın yolu , bir nesne atıldıktan sonra onunla çalışmamanız gerektiğidir (bunun dışında, Disposeyöntemini istediğiniz sıklıkta çağırmanıza izin verilir ).

İfadeye her geldiğinizde açıkça yeni bir şey oluşturduğunuz için, aynı nesneyi asla iki kez kullanmazsınız. Ve amacı başka bir nesneyi yönetmek olduğu içinFrobbleJanitorusingFrobbeJanitorDispose bu ("yönetilen") kaynağı serbest bırakma görevine uygun görünüyor.

(Btw., DisposeHemen hemen her zaman doğru uygulamayı gösteren standart örnek kod , yalnızca dosya sistemi tanıtıcıları gibi yönetilmeyen kaynakların değil, yönetilen kaynakların da serbest bırakılması gerektiğini önerir.)

Kişisel olarak endişelendiğim tek şey, ne olduğu ve operasyonların doğrudan görülebildiği using (var janitor = new FrobbleJanitor())daha açık bir try..finallybloktan daha az net olmasıdır . Ancak hangi yaklaşımın benimseneceği muhtemelen kişisel tercihlere bağlıdır.LockUnlock


1

Kötüye kullanım değil. Onları yaratıldıkları için kullanıyorsunuz. Ancak ihtiyaçlarınıza göre birbirini düşünmeniz gerekebilir. Örneğin, "artistry" yi seçiyorsanız, "kullanma" yı kullanabilirsiniz, ancak kod parçanız birçok kez çalıştırılırsa, performans nedeniyle "deneyin" .. "en sonunda" yapılarını kullanabilirsiniz. Çünkü 'kullanmak' genellikle bir nesnenin yaratılmasını içerir.


1

Bence doğru yaptın. Dispose () aşırı yükleme, aynı sınıfın daha sonra temizlemek zorunda olduğu bir problemdi ve bu temizlemenin ömrü, kilit tutmayı beklediğiniz zamandan farklı olacak şekilde değişti. Ancak, yalnızca Frobble'ı kilitlemek ve kilidini açmaktan sorumlu olan ayrı bir sınıf (FrobbleJanitor) oluşturduğunuz için, işler yeterince ayrıştırılır, bu sorunu çözmezsiniz.

FrobbleJanitor'ı muhtemelen FrobbleLockSession gibi bir şeye yeniden adlandırırdım.


Yeniden adlandırma hakkında - Kilitleme / Kilit Açma ile uğraşmamın birincil nedeni olarak "Kapıcı" kullandım. Diğer isim seçenekleriyle kafiyeli olduğunu düşündüm. ;-) Bu yöntemi kod tabanımın tamamında bir model olarak kullanacak olsaydım, "Oturum" da ima ettiğiniz gibi, kesinlikle daha genel olarak açıklayıcı bir şey eklerdim. Bu eski C ++ günleri olsaydı, muhtemelen FrobbleUnlockScope kullanırdım.
Johann Gerell
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.