c# - هل يجب علي الاتصال بـ Close()أو Dispose()لكائنات البث؟




stream idisposable streamreader streamwriter (5)

تقوم فئات مثل Stream و StreamReader و StreamWriter بتنفيذ واجهة IDisposable . وهذا يعني أنه يمكننا استدعاء طريقة Dispose() على كائنات من هذه الفئات. لقد قاموا أيضًا بتعريف طريقة public تسمى Close() . الآن هذا يربكني ، ما الذي يجب أن أسميه بمجرد انتهائي من الأشياء؟ ماذا لو اتصلت بهما؟

رمزي الحالي هو:

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}

كما ترين ، لقد كتبت using() بنيات ، والتي تقوم تلقائيًا باستدعاء أسلوب Dispose() على كل كائن. لكنني أيضا استدعاء أساليب Close() . هل هذا صحيح؟

يرجى اقتراح أفضل الممارسات عند استخدام كائنات البث. :-)

مثال MSDN لا يستخدم using() بنيات ، وطريقة Close() :

هل هذا جيد؟


Answers

في العديد من الفئات التي تدعم كل من أساليب "الإغلاق والتخلص" ، ستكون المكالمتين متساويتين. في بعض الفئات ، ومع ذلك ، من الممكن إعادة فتح كائن تم إغلاق Close'd. بعض هذه الطبقات قد تبقي بعض الموارد على قيد الحياة بعد الإغلاق ، من أجل السماح بإعادة فتح. قد لا يحتفظ الآخرون بأي موارد على قيد الحياة عند الإغلاق ، ولكن قد يضعوا علامة على Dispos (رفض) لمنع إعادة فتحها بشكل صريح.

يتطلب العقد الخاص بـ IDisposable.Dispose صراحة أن استدعائه على كائن لن يتم استخدامه مرة أخرى أبداً سيكون في أسوأ حالاته غير مؤذية ، لذلك أوصي بالاتصال إما بتعريف قابل للتعميم.أسلوب أو طريقة تسمى Dispose على كل كائن قابل للتعريف ، سواء كان ذلك الشخص أو لا المكالمات


تقول الوثائق أن هاتين الطريقتين متساويتان:

StreamReader.Close : هذا التطبيق من إغلاق المكالمات الأسلوب Dispose تمرير قيمة true.

StreamWriter.Close : هذا التطبيق من إغلاق المكالمات الأسلوب Dispose تمرير قيمة true.

Stream.Close : Stream.Close هذه الطريقة التخلص ، مع تحديد true لتحرير جميع الموارد.

لذلك ، كلاهما صحيح على حد سواء:

/* Option 1 */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2 */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

أنا شخصياً ألتزم بالخيار الأول ، لأنه يحتوي على "ضجيج" أقل.


يظهر الانتقال السريع إلى Reflector.NET أن أسلوب Close() على StreamWriter هو:

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

و StreamReader هو:

public override void Close()
{
    this.Dispose(true);
}

Dispose(bool disposing) في StreamReader هو:

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {
            this.stream = null;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}

أسلوب StreamWriter مشابه.

لذا ، عند قراءة الرمز ، من الواضح أنه يمكنك الاتصال بـ Close() & Dispose() على التدفقات كلما أردت ، وبأي ترتيب. لن يغير السلوك بأي طريقة.

لذلك ، يعود الأمر إلى ما إذا كان من الأفضل قراءة Dispose() و Close() و / أو using ( ... ) { ... } .

أفضلي الشخصي هو أن using ( ... ) { ... } يجب أن يستخدم دائما عندما يكون ذلك ممكنا لأنه يساعدك على "عدم الجري بالمقص".

ولكن ، في حين أن هذا يساعد على تصحيح الأخطاء ، إلا أنه يقلل من إمكانية القراءة. في C # لدينا بالفعل عدد كبير من إغلاق الأقواس المتعرجة ، فكيف لنا أن نعرف أي واحد ينفذ بالفعل على الدفق؟

لذلك أعتقد أنه من الأفضل القيام بذلك:

using (var stream = ...)
{
    /* code */

    stream.Close();
}

لا يؤثر على سلوك التعليمة البرمجية ، ولكنه يؤثر على سهولة القراءة.


لا ، لا يجب عليك استدعاء هذه الطرق يدويًا. في نهاية كتلة الاستخدام يتم استدعاء طريقة التخلص تلقائيًا والتي ستهتم بموارد مجانية غير مدارة (على الأقل لفئات .NET BCL القياسية مثل التدفقات والقراء / الكتّاب ...). لذا يمكنك أيضًا كتابة شفرتك على النحو التالي:

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }

يستدعي أسلوب Close Dispose.


التخلص من تيار يغلق (وربما لا تفعل الكثير من ذلك بكثير.) إغلاق تيار مسحه، وإطلاق أي الموارد ذات الصلة إلى تيار، مثل مقبض ملف. تدفق تيار يأخذ أي البيانات المخزنة التي لم يكتب بعد، ويكتب بها على الفور. بعض التيارات استخدام التخزين المؤقت داخليا لتجنب إجراء طن من التحديثات الصغيرة للموارد باهظة الثمن نسبيا مثل ملف القرص أو أنبوب الشبكة.

تحتاج إلى استدعاء إما Close أو Dispose على معظم تيارات، أو التعليمات البرمجية غير صحيحة، لأن المورد الأساسي لن يتم تحريرها لشخص آخر لاستخدامها حتى يأتي جامع القمامة (الذي يعرف كم من الوقت سوف يستغرق.) Dispose هو ويفضل أن يكون ذلك بطبيعة الحال؛ فمن المتوقع أن تتخلص من جميع الأشياء التي يمكن التخلص منها في C #. ربما لم يكن لديك لاستدعاء Flush صراحة في معظم السيناريوهات.

في C #، انها الاصطلاحية لاستدعاء Dispose عن طريق كتلة using ، وهو السكر النحوي لمحاولة في النهاية كتلة الذي يتصرف في النهاية، على سبيل المثال:

using (FileStream stream = new FileStream(path))
{
    // ...
}

هو متطابق وظيفيا ل

FileStream stream;

try
{
    stream = new FileStream(path);
    // ...
}
finally
{
    if (stream != null)
        stream.Dispose();
}




c# stream idisposable streamreader streamwriter