لماذا لا يمكنني الحصول على أساليب ثابتة مجردة في C#؟

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

  •  08-06-2019
  •  | 
  •  

سؤال

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

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

المحلول

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

يتم استدعاء الطريقة الثابتة من خلال اسم الفئة، وليس من خلال مرجع كائن، وسيقوم كود اللغة الوسيطة (IL) لاستدعائها باستدعاء الطريقة المجردة من خلال اسم الفئة التي حددتها، وليس بالضرورة اسم الفئة الصف الذي استخدمته.

اسمحوا لي أن أعرض مثالا.

مع الكود التالي:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

إذا اتصلت بـ B.Test، مثل هذا:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

ثم الكود الفعلي داخل الطريقة الرئيسية هو كما يلي:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

كما ترون، تم إجراء الاتصال بـ A.Test، لأن الفئة A هي التي حددته، وليس B.Test، على الرغم من أنه يمكنك كتابة الكود بهذه الطريقة.

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

أدرك أن مصممي IL قد يسمحون بتجميع التعليمات البرمجية لاستدعاء B.Test، وحل الاستدعاء في وقت التشغيل، لكنه لا يزال غير افتراضي، حيث سيتعين عليك كتابة نوع من اسم الفئة هناك.

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

وبالتالي، فإن الأساليب الثابتة الافتراضية/المجردة غير متوفرة في .NET.

نصائح أخرى

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

افترض أنه يمكنك، للحظة، وراثة الأساليب الثابتة.تخيل هذا السيناريو:

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

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

قال مشارك آخر (ماكدويل) إن تعدد الأشكال يعمل فقط مع مثيلات الكائن.ينبغي أن يكون مؤهلا؛هناك لغات تتعامل مع الفئات على أنها مثيلات لنوع "Class" أو "Metaclass".تدعم هذه اللغات تعدد الأشكال لكل من أساليب المثيل والفئة (الثابتة).

C#، مثل Java وC++ قبلها، ليست مثل هذه اللغة؛ال static يتم استخدام الكلمة الأساسية بشكل صريح للإشارة إلى أن الطريقة مرتبطة بشكل ثابت وليست ديناميكية/افتراضية.

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

في ما يلي موقف حيث توجد بالتأكيد حاجة إلى وراثة الحقول والأساليب الثابتة:

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?

نحن في الواقع نتجاوز الأساليب الثابتة (في دلفي)، إنها قبيحة بعض الشيء، ولكنها تعمل بشكل جيد لتلبية احتياجاتنا.

نستخدمها حتى تتمكن الفئات من الحصول على قائمة بالكائنات المتاحة لها دون مثيل الفئة، على سبيل المثال، لدينا طريقة تبدو كما يلي:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

إنه أمر قبيح ولكنه ضروري، وبهذه الطريقة يمكننا إنشاء مثيل لما هو مطلوب فقط، بدلاً من إنشاء مثيل لجميع الفئات فقط للبحث عن الكائنات المتاحة.

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

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

أتمنى أن المثال كان واضحا.

الأساليب المجردة افتراضية ضمنيًا.تتطلب الأساليب المجردة مثيلًا، لكن الأساليب الثابتة لا تحتوي على مثيل.لذلك، يمكن أن يكون لديك طريقة ثابتة في فئة مجردة، ولا يمكن أن تكون مجردة ثابتة (أو ثابتة مجردة).

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