كود - لماذا لا تسمح C#بالطرق الثابتة لتطبيق واجهة؟




كود سي شارب (16)

As per Object oriented concept Interface implemented by classes and have contract to access these implemented function(or methods) using object.

So if you want to access Interface Contract methods you have to create object. It is always must that is not allowed in case of Static methods. Static classes ,method and variables never require objects and load in memory without creating object of that area(or class) or you can say do not require Object Creation.

لماذا تم تصميم C # بهذه الطريقة؟

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

إذا رغبت الفئات في تنفيذ هذا السلوك بطريقة مشتركة ، فلماذا لا تفعل ذلك؟

هذا مثال على ما يدور في ذهني:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }

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


إلى الحد الذي تمثل فيه واجهات "العقود" ، يبدو هادئًا بشكل معقول لفئات ثابتة لتنفيذ واجهات.

يبدو أن كل الحجج السابقة تفوت هذه النقطة حول العقود.


السبب التقني (المبسط) هو أن الأساليب الثابتة ليست في vtable ، ويتم اختيار موقع الاتصال في وقت التحويل البرمجي. هذا هو السبب نفسه الذي لا يمكنك تجاوزه أو جعله أعضاءًا افتراضيًا ثابتًا. لمزيد من التفاصيل ، ستحتاج إلى غراد CS أو grad - وليس لدي.

للسبب السياسي ، سوف أقتبس من إريك ليبرت (وهو من رحم المجمع ، وحاصل على بكالوريوس في الرياضيات ، وعلوم الكمبيوتر والرياضيات التطبيقية من جامعة واترلو (المصدر: LinkedIn ):

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

لاحظ أن Lippert يترك مساحة لطريقة الكتابة المسماة:

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

لكن لم يقتنع بعد بفائدتها.


على افتراض أنك تسأل لماذا لا يمكنك القيام بذلك:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

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

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

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

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


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

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

هنا لا يوجد تعدد الأشكال ، يستخدم النوع العام النوع الفعلي للكائن ويستدعي + = عامل ، ولكن هذا يفشل لأنه لا يستطيع التأكد من وجود ذلك المشغل. الحل البسيط هو تحديده في القيد ؛ الحل المستحيل بسيط لأن المشغلين هم أساليب ثابتة وثابتة لا يمكن أن تكون في واجهة و (هنا هي المشكلة) يتم تمثيل القيود كواجهات.

ما يحتاج إليه C # هو نوع من القيود الحقيقية ، كما أن جميع الواجهات قد تكون أيضًا قيودًا ، ولكن لا تكون جميع القيود واجهات ، فيمكنك القيام بذلك:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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


لأن الواجهات في بنية الوراثة ، والطرق الثابتة لا ترث بشكل جيد.


لإعطاء مثال حيث أفتقد التنفيذ الثابت لأساليب الواجهة أو ما قدمه مارك براكيت كـ "طريقة الكتابة المسماة":

عند القراءة من تخزين قاعدة بيانات ، لدينا فئة DataTable عامة تعالج القراءة من جدول أي بنية. يتم وضع جميع المعلومات الخاصة بالجدول في فصل واحد لكل جدول يحتوي أيضًا على بيانات لصف واحد من قاعدة البيانات والتي يجب أن تقوم بتطبيق واجهة IDataRow. تضمين في IDataRow هو وصف بنية الجدول لقراءة من قاعدة البيانات. يجب أن يطلب DataTable من datastructure من IDataRow قبل القراءة من قاعدة البيانات. حاليا هذا يبدو:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

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


ما يبدو أنك تريده سيسمح باستدعاء طريقة ثابتة عبر النوع أو أي مثيل من هذا النوع. هذا من شأنه أن يؤدي على الأقل إلى الغموض الذي ليس سمة مرغوبة.

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

ومن المحتمل أيضًا أن يفقد المُصنّف الذي يتطابق مع هذه الرغبة بعض التحسينات التي قد تأتي مع فصل أكثر صرامة بين الطرق الثابتة والساكنة.


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

كيف تسميها؟

public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

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

لنفترض أن لدينا معلمة type في الطريقة العامة ونحتاج إلى إجراء بعض العمليات بها. نحن لا نريد أن نجعل ، لأننا غير واعين للمبنيين.

فمثلا:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

لسوء الحظ ، لا يمكنني تقديم سوى بدائل "قبيحة":

  • استخدام انعكاس قبيحة ويتفوق على فكرة واجهات وتعدد الأشكال.

  • إنشاء فصل مصنع منفصل تمامًا

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

  • قم بتحديث ثم قم باستدعاء طريقة الواجهة المطلوبة

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

مثال:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

من أجل استخدام Instantination لحل مشكلة واجهة ثابتة ، نحتاج إلى القيام بما يلي:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

ومن الواضح أن هذا قبيح وغير ضروري أيضا يعقد رمز لجميع الطرق الأخرى. من الواضح ، ليس حلا رائعا سواء!


يبدو أن معظم الناس ينسون أنه في فئات OOP هي كائنات أيضًا ، ولذلك لديهم رسائل ، والتي لسبب ما يدعو c # "طريقة ثابتة". حقيقة وجود اختلافات بين كائنات المثال وكائنات الطبقة تظهر فقط العيوب أو أوجه القصور في اللغة. متفائل حول ج # على الرغم من ...


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


C# and the CLR should support static methods in interfaces as Java does. The static modifier is part of a contract definition and does have meaning, specifically that the behavior and return value do not vary base on instance although it may still vary from call to call.

That said, I recommend that when you want to use a static method in an interface and cannot, use an annotation instead. You will get the functionality you are looking for.


OK here is an example of needing a 'type method'. I am creating one of a set of classes based on some source XML. So I have a

  static public bool IsHandled(XElement xml)

function which is called in turn on each class.

The function should be static as otherwise we waste time creating inappropriate objects. As @Ian Boyde points out it could be done in a factory class, but this just adds complexity.

It would be nice to add it to the interface to force class implementors to implement it. This would not cause significant overhead - it is only a compile/link time check and does not affect the vtable.

However, it would also be a fairly minor improvement. As the method is static, I as the caller, must call it explicitly and so get an immediate compile error if it is not implemented. Allowing it to be specified on the interface would mean this error comes marginally earlier in the development cycle, but this is trivial compared to other broken-interface issues.

So it is a minor potential feature which on balance is probably best left out.


The fact that a static class is implemented in C# by Microsoft creating a special instance of a class with the static elements is just an oddity of how static functionality is achieved. It is isn't a theoretical point.

An interface SHOULD be a descriptor of the class interface - or how it is interacted with, and that should include interactions that are static. The general definition of interface (from Meriam-Webster): the place or area at which different things meet and communicate with or affect each other. When you omit static components of a class or static classes entirely, we are ignoring large sections of how these bad boys interact.

Here is a very clear example of where being able to use interfaces with static classes would be quite useful:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

Currently, I write the static classes that contain these methods without any kind of checking to make sure that I haven't forgotten anything. Is like the bad old days of programming before OOP.







language-features