أساليب التوسيع (ارشادات برمجة C#)

تمكنك أساليب التوسيع من "إضافة" أساليب إلى أنواع موجودة بدون إنشاء نوع مشتق جديد أو إعادة الترجمة أو تعديل النوع الأصلي. أساليب التوسيع هي نوع خاص من أسلوب ثابت, ولكن تعتبر كما لو كانت أساليب مثيلة في النوع الموسّع. بالنسبة لتعليمات العميل البرمجية المكتوبة في C# و Visual Basic "، فلا يوجد اختلاف ظاهري بين استدعاء أسلوب التوسيع و الأساليب التي تم تعريفها بالفعل في النوع.

أساليب التوسيع الأكثر شيوعاً هي مشغلات الاستعلامات القياسية LINQ التي تقوم باضافة استعلام وظيفي لـ System.Collections.IEnumerable الموجود و أنواع System.Collections.Generic.IEnumerable<T> . لاستخدام مشغلات الاستعلام القياسية, أولاً لإحضارهم إلى نطاق بتوجيه using System.Linq. و من ثم فإن أي نوع يقوم بتطبيق IEnumerable<T> يبدو أنه يملك أساليب مثيل مثل GroupBy, OrderBy, Average، و هكذا. يمكنك أن ترى هذه الأساليب إضافية في إكمال العبارة التحسس عند كتابة "نقطه" بعد مثيل لنوع IEnumerable<T> مثل List<T> أو Array.

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

class ExtensionMethods2    
{

    static void Main()
    {            
        int[] ints = { 10, 45, 15, 39, 21, 26 };
        var result = ints.OrderBy(g => g);
        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }           
    }        
}
//Output: 10 15 21 26 39 45

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

يظهر المثال التالي أسلوب التوسيع المعرّفة من أجل فئة System.String. لاحظ تعريفه داخل فئة غير متداخلة، وغير عامة ثابتة:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}

يمكن تقديم أسلوب التوسيع WordCount في نطاق مع هذا التوجيه using:

using ExtensionMethods;

كما أنه يمكن استدعاؤها من أحد التطبيقات باستخدام بناء الجملة التالي:

string s = "Hello Extension Methods";
int i = s.WordCount();

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

لمزيد من المعلومات، راجع كيفية القيام بما يلي: تنفيذ واستدعاء أسلوب التوسيع المخصص (ارشادات برمجة C#).

بشكل عام، من المحتمل أن تقوم باستدعاء أساليب التوسيع الآن أكثر من تطبيق أساليب التوسيع الخاصة بك. لأنه يتم استدعاء أساليب التوسيع باستخدام بناء جملة أسلوب مثيل, فلا يوجد معرفة خاصة مطلوبة لاستخدامها من تعليمات العميل البرمجية. لتمكين أساليب توسيع لنوع معيّن، فقط قم بإضافة توجيه using لمساحة الاسم للطرق التي تم تعريفها. على سبيل المثال، لاستخدام مشغل الاستعلامات القياسي, قم بإضافة هذا التوجيه using إلى التعليمات البرمجية:

using System.Linq;

(قد يجب أيضاً عليك إضافة مرجع إلى نظام.Core.dll.) وسلاحظ أن مشغلات الاستعلام القياسي ستظهر الآن في قائمة IntelliSense كأساليب إضافية متوفرة لمعظم أنواع IEnumerable<T> .

ملاحظة

على الرغم من أن مشغلات الاستعلام القياسية لا تظهر في قائمة IntelliSense لـ String، إلا أنها تظل متوفرة.

ربط أساليب التوسيع في وقت التحويل برمجياً

يمكنك استخدام أساليب التوسيع لتوسيع فئة أو واجهة، ولكن ليس لمنعهم. لن يتم استدعاء أسلوب التوسيع مع نفس الاسم و نفس التوقيع كما في أسلوب الواجهة أو الفئة. في وقت الترجمة، أساليب التوسيع لها دائماً أولوية أقل من أساليب المثيل المعرفة في النوع نفسه. بمعنى آخر، إذا وجد نوع له أسلوب باسم Process(int i)، ثم لديك أسلوب التوسيع مع نفس التوقيع, فإن المحوّل البرمجي سيقوم دوماً برتبط مثيل الأسلوب. عندما يصادف المحول البرمجي استدعاء أسلوب, فإنه يبحث أولاً عن تطابق في نوع أساليب المثيل. إذا لم يتم العثور على أي تطابق، فإنه سوف يبحث عن أي أساليب توسيع التي تم تعريفها للنوع، وربطها بأسلوب التوسيع الأول الذي عثر عليه. يوضح المثال التالي كيفية تحديد المحول البرمجي الذي سيربط أسلوب التوسيع أو مثيل الأسلوب.

مثال

يوضح المثال التالي قواعد يتبعها مترجم C# في تحديد ما إذا كنت تريد ربط استدعاء أسلوب لأسلوب مثيل للنوع, أو لأسلوب التوسيع. يحتوي ثابت الفئة Extensions على أساليب توسيع معرّفة لي نوع يقوم بتطبيق IMyInterface. فئات A, B، و C تقوم كلها بتطبيق الواجهة.

لا يتم استدعاء أسلوب MethodB لأن اسمه و توقيعه تطابق الأساليب المطبقة مسبقاً بواسطة الفئات.

عندما يتعذر على المحول البرمجي العثور على أسلوب مثيل بتوقيع مطابق, فإنه يقوم بربطه لأسلوب التوسيع مطابق إن وجد.

namespace Extensions
{
  using System;
  using ExtensionMethodsDemo1;

     // Define extension methods for any type that implements IMyInterface.
     public static class Extension
     {
        public static void MethodA(this IMyInterface myInterface, int i)
        {
            Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, int i)");
        }

        public static void MethodA(this IMyInterface myInterface, string s) 
        {
            Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, string s)");
        }

        // This method is never called, because the three classes implement MethodB.
        public static void MethodB(this IMyInterface myInterface) 
        {
            Console.WriteLine("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}
namespace ExtensionMethodsDemo1
{
    using System;
    using Extensions;

    public interface IMyInterface
    {
        void MethodB();
    }

    class A : IMyInterface 
    {
        public void MethodB(){Console.WriteLine("A.MethodB()");}
    } 

    class B : IMyInterface
    {
        public void MethodB() { Console.WriteLine("B.MethodB()"); }
        public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
    }

    class C : IMyInterface
    {
        public void MethodB() { Console.WriteLine("C.MethodB()"); }
        public void MethodA(object obj) { Console.WriteLine("C.MethodA(object obj)"); }
    }

    class ExtMethodDemo
    {
        static void Main(string[] args)
        {
            A a = new A();
            B b = new B();
            C c = new C();
            TestMethodBinding(a,b,c);
        }

        static void TestMethodBinding(A a, B b, C c)
        {
            // A has no methods, so each call resolves to 
            // the extension methods whose signatures match.
            a.MethodA(1);           // Extension.MethodA(object, int)
            a.MethodA("hello");     // Extension.MethodA(object, string)
            a.MethodB();            // A.MethodB()

            // B itself has a method with this signature.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()

            // B has no matching method, but Extension does.
            b.MethodA("hello");     // Extension.MethodA(object, string)

            // In each case C has a matching instance method.
            c.MethodA(1);           // C.MethodA(object)
            c.MethodA("hello");     // C.MethodA(object)
            c.MethodB();            // C.MethodB()
        }
    }
}
/* Output:
    Extension.MethodA(this IMyInterface myInterface, int i)
    Extension.MethodA(this IMyInterface myInterface, string s)
    A.MethodB()
    B.MethodA(int i)
    B.MethodB()
    Extension.MethodA(this IMyInterface myInterface, string s)
    C.MethodA(object obj)
    C.MethodA(object obj)
    C.MethodB()
 */

إرشادات عامة

بشكل عام، نوصي بتطبيق أساليب التوسيع بقلة و فقط عندما تكون بحاجة لذلك. تعليمات العميل البرمجية التي يجب أن تمتد إلى النوع الموجود كلما كان ذلك ممكناً، ينبغي القيام بذلك عن طريق إنشاء نوع جديد مشتق من النوع الموجود. لمزيد من المعلومات، راجع الوراثة (دليل البرمجة لـ #C).

عند استخدام أسلوب التوسيع لتوسيع التعليمات البرمجية المصدر التي لا يمكنك تغيير نوعها, فإنك تقوم بتشغيل خطر يؤدي إلى تغيير في تطبيق النوع الذي سيسبب في فصل في أسلوب التوسيع.

إذا قمت بتطبيق أساليبالتوسيع لنوع معطى، تذكر النقطتين التاليتين:

  • لن يتم استدعاء أسلوب التوسيع إذا كان له نفس التوقيع كالأسلوب المعرف في النوع.

  • يتم إحضار أساليب التوسيع في نطاق مستوى مساحة الاسم. على سبيل المثال، إذا كان لديك عدة فئات ثابتة التي تتضمن أساليب التوسيع في مساحة اسم واحدة باسم Extensions، فإنه سيتم إحضارها كلها إلى نطاق بواسطة توجيه using Extensions; .

راجع أيضًا:

المرجع

تعبيرات لامدا (ارشادات برمجة C#)

المبادئ

دليل البرمجة لـ #C

نظرة عامة على عوامل تشغيل الاستعلام القياسية

موارد أخرى

قواعد التحويل لمعلمات المثيل وتأثيرها

إمكانية التشغيل التفاعلي لأساليب التوسيع بين اللغات

أساليب التوسيع و تفويض المحولات

ربط أسلوب التوسيع و التقرير عن خطأ