18.07.2024 5 min read

إدارة الموارد باستخدام Using في C#

بواسطة Valens

كيف تتيح عبارة using في C# التخلص التلقائي من الموارد غير المُدارة ومنع تسرّبات الذاكرة.

C# using statement banner

عبارة using أداة قوية للتخلص التلقائي من الموارد غير المستخدَمة.

يعمل التنفيذ الأساسي على النحو التالي:

using (var ch = new CustomHandler())
{
    /* Do something with ch */
}

تفيد عبارة using في منع تسرّبات الذاكرة من خلال التعامل مع الموارد غير المُدارة مثل:

  • إغلاق الملفات التي لم تعد مستخدَمة
  • إغلاق اتصالات قاعدة البيانات
  • التعامل مع إنهاء HTTP Client

لاستخدام عبارة using مع صنف معيّن، يجب أن ينفّذ ذلك الصنف واجهة IDisposable:

class CustomHandler : IDisposable
{
    public void SayHello()
    {
        Console.WriteLine("Hello World!");
    }

    public void Dispose()
    {
        Console.WriteLine("Handled dispose!");
    }
}

يمكن التحقق من التخلص التلقائي عن طريق رمي استثناء داخل الدالة SayHello.

class CustomHandler : IDisposable
{
    public void SayHello()
    {
        throw new Exception("Something went wrong!");
    }

    public void Dispose()
    {
        Console.WriteLine("Handled dispose!");
    }
}

أنشأت صنفًا جديدًا باسم Main ينشئ نسخة من CustomHandler.

بدون Using

الاختبار الأول، بدون using.

public class Main
{
    public void LearnUsing()
    {
        var ch = new CustomHandler();
        ch.SayHello();
    }
}

var m = new Main();
m.LearnUsing();

يُرمى الاستثناء وتُنهى العملية كما هو متوقع، لكن لم يتم التخلص من أي موارد.

Unhandled exception. System.Exception: Something went wrong!
Process finished.

مع Using

public class Main
{
    public void LearnUsing()
    {
        using (var ch = new CustomHandler())
        {
            ch.SayHello();
        }
    }
}

var m = new Main();
m.LearnUsing();

هذه المرة تم التخلص من الموارد بنجاح.

Unhandled exception. System.Exception: Something went wrong!
Handled dispose!
Process finished.

التخلص اليدوي

على الأرجح تسأل نفسك الآن: لماذا لا نستخدم كتلة Try-Catch-Finally؟ هذا يعمل أيضًا، لكن Try-Catch لا تتخلص من الموارد. إنها تتعامل فقط مع الأخطاء.

للتخلص من الموارد داخل Try-Catch، يجب استدعاء دالة dispose يدويًا:

var ch = new CustomHandler();
try
{
    ch.SayHello();
}
catch (Exception ex)
{
    /* Handle exception */
}
finally
{
    ch.Dispose();
}

بينما تستدعي عبارة using دالة Dispose تلقائيًا.

استخدام Using في الأصناف الموجودة

كما ذُكر، يمكن استخدام أي صنف ينفّذ واجهة IDisposable داخل كتلة using.

FileStream

using (FileStream fs = new FileStream("greetings.txt", FileMode.Open))
{
    /* Write to file */
}

HTTPClient

public async Task LearnUsing()
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("/api/getData");

        if (response.IsSuccessStatusCode)
        {
            /* Handle response */
        }
    }
}

فقط تأكد من إضافة الكلمة المفتاحية await قبل استدعاء LearnUsing، لأنها الآن تُعيد Task.

await m.LearnUsing();

اتصال SQL

تُستخدم عبارة using أيضًا عند تنفيذ استعلامات قاعدة البيانات دون استخدام أي أطر عمل. للبدء، ثبّت حزمة System.Data.SqlClient عبر مدير حزم NuGet واستوردها كتبعية.

System Data SQL package

ثم أعدّ سلسلة اتصال بقاعدة بياناتك. لهذا الغرض أنشأت قاعدة بيانات محلية عبر SQL Server.

Project tree
string connectionString = "Server=;Database=;Integrated Security=True;";
string connectionString = "Server=DESKTOP-F5;Database=Games;Integrated Security=True;";

الآن باستخدام مجموعة من عبارات using:

  • إنشاء اتصال بقاعدة البيانات باستخدام SqlConnection
  • تنفيذ استعلام SQL باستخدام SqlCommand
  • قراءة الاستجابة من الجدول باستخدام SqlDataReader

هنا يمكنك رؤية كيفية تداخل عبارات using.

string connectionString = "Server=DESKTOP-F5;Database=Games;Integrated Security=True;";

using (SqlConnection connection = new SqlConnection(connectionString))
{
    try
    {
        await connection.OpenAsync();

        string sqlQuery = "SELECT * FROM Game_Types";
        using (SqlCommand command = new SqlCommand(sqlQuery, connection))
        {
            using (SqlDataReader reader = await command.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    Console.WriteLine(reader["Title"]);
                }
            }
        }
    }
    catch (SqlException ex)
    {
        Console.WriteLine(ex.Message);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

ملاحظة: إذا كنت تستخدم Entity Framework، فلا حاجة لتغليف الاستعلامات بـ using. يتخلص صنف DbContext تلقائيًا من الموارد.

للتخلص الفوري، يمكن أيضًا استخدام عبارة using ضمن سطر واحد دون أقواس معقوفة.

using (var fs = new FileStream("my-file.txt", FileMode.Open))
    Console.WriteLine("File open!");

هذا كل ما لدي لهذا اليوم. لا تنسَ الضغط على زر المتابعة.

قراءات إضافية عرض الكل
الخطوة التالية

طبّق هذه الأفكار على منتجك

يساعد استوديو فالنس الفرق ذات المخاطر العالية على تحويل الوضوح الهندسي إلى تسليم منتج معياري.