Expression<Func<T>> مقابل Func<T>: سر التحسين في لغة C#

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Expression\n\n

تخيل: أنت تكتب كود C#. تحتاج إلى تمرير شرط تصفية إلى دالة. ماذا تختار: Func<User, bool> أم Expression<Func<User, bool>>؟ يبدو الفرق بسيطًا — كلاهما يقبل مستخدمًا ويعيد bool. لكن خلف الكواليس يكمن عالم كامل من الاختلافات.

\n\n

على Stack Overflow، حصد هذا السؤال آلاف المشاهدات — وليس بدون سبب. اليوم سنفهم لماذا يمكن أن يكون Expression<Func<T>> أقوى وأسرع وأكثر أمانًا من Func<T> العادي. والأهم من ذلك — متى نستخدم كلًا منهما لتجنب المشاكل.

\n\n

شرح سريع: ما هما Func و Expression؟

\n\n

Func<T> هو مجرد مفوض. دالة جاهزة للتنفيذ، كود آلي مُجمّع. تمرر إليه البيانات — فيقوم بمعالجتها. سريع، فعال، لكن... غير شفاف.

\n\n
Func<User, bool> isAdult = user => user.Age >= 18;\nbool result = isAdult(someUser); // يتم التنفيذ فورًا
\n\n

Expression<Func<T>> هو شجرة تعبيرات. ليس كودًا، بل وصف له. لا يمكنك استدعاؤه مباشرة — بل تحتاج إما إلى تجميعه إلى مفوض (عبر .Compile()) أو تحليل هيكله.

\n\n
Expression<Func<User, bool>> expr = user => user.Age >= 18;\n// لا يمكن استدعاؤه مباشرة: expr(someUser) — خطأ في التجميع\nvar compiled = expr.Compile();\nbool result = compiled(someUser); // الآن يمكن
\n\n

قد يبدو هذا مجهودًا إضافيًا غير ضروري؟ لكن هذه "الوصفية" هي التي تفتح الباب للسحر.

\n\n

السيناريو 1: LINQ to Objects (العمل في الذاكرة)

\n\n

عند العمل مع المجموعات في الذاكرة (مثل List<T>)، يستخدم LINQ to Objects Func<T>. ليست هناك حاجة لأشجار التعبيرات — كل شيء يُنفذ في نفس العملية.

\n\n
var users = new List<User>();\nvar adults = users.Where(u => u.Age >= 18).ToList(); // Func<User, bool>
\n\n

الأداء ممتاز: لا تكاليف إضافية لتحليل الشجرة. إذا كنت تعمل فقط مع بيانات محلية — استخدم Func بثقة. إنه أسرع وأبسط.

\n\n

السيناريو 2: LINQ to SQL / Entity Framework (العمل مع قاعدة البيانات)

\n\n

وهنا يبدأ السحر. عندما تكتب:

\n\n
var adults = dbContext.Users.Where(u => u.Age >= 18).ToList();
\n\n

Entity Framework لا ينفذ كود C# الخاص بك. بدلاً من ذلك، يقوم بتحليل شجرة التعبيرات المُمررة وتحويلها إلى استعلام SQL:

\n\n
SELECT * FROM Users WHERE Age >= 18
\n\n

إذا كنت قد مررت Func، لما استطاع EF "النظر إلى الداخل" — لكان قد حمّل جميع المستخدمين من الجدول ونفذ التصفية في الذاكرة. تخيل: جدول يحتوي على 10 ملايين سجل — وكلها تنتقل عبر الشبكة إلى تطبيقك لتُصفى على العميل.

\n\n

Expression<Func<T>> يسمح لـ EF (أو أي مزود IQueryable آخر) بقراءة شرطك، وترجمته إلى لغة قاعدة البيانات (SQL، استعلام MongoDB، OData، إلخ) وتنفيذ التصفية على الخادم.

\n\n

الأداء: اختبارات الأداء والأرقام الحقيقية

\n\n

لنلقِ نظرة على الأرقام. لنأخذ مثالًا بسيطًا: تصفية 100,000 مستخدم حسب العمر.

\n\n
// الخيار 1: Func — تحميل الكل، التصفية في الذاكرة\nvar allUsers = dbContext.Users.ToList(); // SELECT * FROM Users\nvar adults1 = allUsers.Where(u => u.Age >= 18).ToList();\n\n// الخيار 2: Expression — التصفية على الخادم\nvar adults2 = dbContext.Users.Where(u => u.Age >= 18).ToList(); // SELECT * FROM Users WHERE Age >= 18
\n\n

النتائج (تقريبية ولكنها توضيحية):

\n
    \n

المدونات

توصيات الكتب