استخدام العائد للتكرار عبر قارئ البيانات قد لا يغلق الاتصال؟

StackOverflow https://stackoverflow.com/questions/47521

  •  09-06-2019
  •  | 
  •  

سؤال

فيما يلي نموذج التعليمات البرمجية لاسترداد البيانات من قاعدة بيانات باستخدام الكلمة الأساسية الخاصة بالعائد والتي وجدتها في مكان قليل أثناء البحث في Google:

public IEnumerable<object> ExecuteSelect(string commandText)
{
    using (IDbConnection connection = CreateConnection())
    {
        using (IDbCommand cmd = CreateCommand(commandText, connection))
        {
             connection.Open();
             using (IDbDataReader reader = cmd.ExecuteReader())
             {
                while(reader.Read())
                {
                    yield return reader["SomeField"];
                }
             }
             connection.Close();
        }
    }
}

هل أنا على صواب في اعتقادي أنه في نموذج التعليمات البرمجية هذا، لن يتم إغلاق الاتصال إذا لم نكرره على قارئ البيانات بأكمله؟

هنا مثال لن يغلق الاتصال، إذا فهمت العائد بشكل صحيح..

foreach(object obj in ExecuteSelect(commandText))
{
  break;
}

بالنسبة لاتصال قاعدة البيانات الذي قد لا يكون كارثيًا، أفترض أن GC سيقوم بتنظيفه في النهاية، ولكن ماذا لو كان موردًا أكثر أهمية بدلاً من الاتصال؟

هل كانت مفيدة؟

المحلول

يقوم المكرر الذي قام المترجم بتوليفه بتنفيذ IDisposable، والذي يستدعي foreach عند الخروج من حلقة foreach.

ستقوم طريقة Dispose() الخاصة بـ Iterator بتنظيف عبارات الاستخدام عند الخروج المبكر.

طالما أنك تستخدم المكرِّر في حلقة foreach، أو تستخدم كتلة()، أو تستدعي طريقة Dispose() بطريقة أخرى، فسيتم تنظيف المكرِّر.

نصائح أخرى

سيتم إغلاق الاتصال تلقائيًا نظرًا لأنك تستخدمه داخل كتلة "الاستخدام".

من الاختبار البسيط الذي قمت بتجربته، aku صحيح، ويتم استدعاء التخلص بمجرد خروج كتلة foreach.

@ ديفيد :ومع ذلك، يتم الاحتفاظ بمكدس الاستدعاءات بين المكالمة، لذلك لن يتم إغلاق الاتصال لأنه في المكالمة التالية سنعود إلى التعليمة التالية بعد العائد، وهي الكتلة while.

ما أفهمه هو أنه عندما يتم التخلص من المكرر، سيتم التخلص من الاتصال به أيضًا.أعتقد أيضًا أنه لن تكون هناك حاجة إلى Connection.Close لأنه سيتم الاهتمام به عند التخلص من الكائن بسبب عبارة الاستخدام.

وهنا برنامج بسيط حاولت اختبار السلوك...

class Program
{
    static void Main(string[] args)
    {
        foreach (int v in getValues())
        {
            Console.WriteLine(v);
        }
        Console.ReadKey();

        foreach (int v in getValues())
        {
            Console.WriteLine(v);
            break;
        }
        Console.ReadKey();
    }

    public static IEnumerable<int> getValues()
    {
        using (TestDisposable t = new TestDisposable())
        {
            for(int i = 0; i<10; i++)
                yield return t.GetValue();
        }
    }
}

public class TestDisposable : IDisposable
{
    private int value;

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }

    public int GetValue()
    {
        value += 1;
        return value;
    }
}

انطلاقا من هذا التفسير الفني, ، لن يعمل الكود الخاص بك كما هو متوقع، ولكن سيتم إحباط العنصر الثاني، لأن الاتصال كان مغلقًا بالفعل عند إرجاع العنصر الأول.

@ جويل جوفرو :نعم، كان ينبغي لي أن أقرأ. الجزء 3 من هذه السلسلة يوضح أن المترجم يضيف معالجة خاصة للكتل الأخيرة ليتم تشغيلها فقط عند حقيقي نهاية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top