Mengapa C # Tidak Memungkinkan Metode Statis untuk Mengimplementasikan Antarmuka?

447

Mengapa C # dirancang seperti ini?

Seperti yang saya pahami, antarmuka hanya menggambarkan perilaku, dan melayani tujuan menggambarkan kewajiban kontrak untuk kelas yang mengimplementasikan antarmuka yang perilaku tertentu diimplementasikan.

Jika kelas ingin mengimplementasikan perilaku itu dalam metode bersama, mengapa tidak?

Ini adalah contoh dari apa yang ada dalam pikiran saya:

// 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;
    }

    ....

 }
Kramii
sumber
6
Nah, Java 8 memilikinya ( stackoverflow.com/questions/23148471/… ).
liang
1
Lihat bagaimana Anda dapat menggabungkan perilaku statis dengan pewarisan atau implementasi antarmuka: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes
1
IListItem.ScreenName() => ScreenName()(menggunakan sintaks C # 7) akan mengimplementasikan metode antarmuka secara eksplisit dengan memanggil metode statis. Hal-hal menjadi jelek ketika Anda menambahkan warisan untuk itu, meskipun (Anda harus mengimplementasikan ulang antarmuka)
Jeroen Mostert
2
Hanya membiarkan semua orang tahu bahwa penantian sudah berakhir! C # 8.0 memiliki metode antarmuka statis: dotnetfiddle.net/Lrzy6y (meskipun mereka bekerja sedikit berbeda dengan cara OP ingin mereka bekerja - Anda tidak harus mengimplementasikannya)
Jamie Twells

Jawaban:

221

Dengan asumsi Anda bertanya mengapa Anda tidak bisa melakukan ini:

public interface IFoo {
    void Bar();
}

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

Ini tidak masuk akal bagi saya, secara semantik. Metode yang ditentukan pada antarmuka harus ada di sana untuk menentukan kontrak untuk berinteraksi dengan objek. Metode statis tidak memungkinkan Anda untuk berinteraksi dengan objek - jika Anda menemukan diri Anda dalam posisi di mana implementasi Anda dapat dibuat statis, Anda mungkin perlu bertanya pada diri sendiri apakah metode itu benar-benar termasuk dalam antarmuka.


Untuk mengimplementasikan contoh Anda, saya akan memberikan Animal properti const, yang masih memungkinkannya diakses dari konteks statis, dan mengembalikan nilai itu dalam implementasi.

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; }
}

Untuk situasi yang lebih rumit, Anda selalu dapat mendeklarasikan metode statis lain dan mendelegasikannya. Dalam mencoba memberikan sebuah contoh, saya tidak dapat memikirkan alasan apa pun Anda akan melakukan sesuatu yang non-sepele dalam konteks statis dan instance, jadi saya akan menghindarkan Anda gumpalan FooBar, dan menganggapnya sebagai indikasi bahwa itu mungkin bukan ide yang bagus.

Chris Marasti-Georg
sumber
6
Contoh sempurna! Tapi saya tidak yakin saya mengerti alasan Anda. Tentunya kompiler dapat dirancang untuk melihat anggota statuc juga? Tidakkah instance memiliki tabel alamat untuk implementasi metode ini? Tidak bisakah metode statis dimasukkan dalam tabel ini?
Kramii
12
Ada kasus di mana ini bisa bermanfaat. Sebagai contoh, saya ingin semua implementator menerapkan metode GetInstance yang mengambil argumen XElement. Saya tidak dapat mendefinisikan itu sebagai metode statis di antarmuka, atau menuntut tanda tangan konstruktor dari antarmuka.
oleks
25
Banyak orang menimbang kedua belah pihak: dari "Ini tidak masuk akal" hingga "Ini bug, saya berharap Anda bisa." (Saya pikir ada kasus penggunaan yang valid untuk itu, yang merupakan cara saya berakhir di sini.) Sebagai solusi, metode instance dapat dengan mudah mendelegasikan ke metode statis.
harpo
5
Anda juga bisa menerapkan potongan sebagai metode ekstensi ke antarmuka dasar, seperti dalam: public static object MethodName(this IBaseClass base)dalam kelas statis. Kekurangannya, bagaimanapun, adalah tidak seperti pewarisan antarmuka - ini tidak memaksa / memungkinkan pewaris individu untuk menimpa metodologi dengan baik.
Troy Alford
8
Itu akan masuk akal dengan obat generik. Misalnya membatalkan sesuatu <T> () di mana T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I
173

Alasan teknis saya (disederhanakan) adalah bahwa metode statis tidak ada dalam vtable , dan situs panggilan dipilih pada waktu kompilasi. Itu alasan yang sama Anda tidak dapat memiliki anggota pengganti atau virtual statis. Untuk lebih jelasnya, Anda memerlukan lulusan CS atau wonk kompiler - yang mana saya tidak memilikinya.

Untuk alasan politik, saya akan mengutip Eric Lippert (yang merupakan kompiler wink, dan memegang gelar Sarjana Matematika, ilmu Komputer dan Matematika Terapan dari University of Waterloo (sumber: LinkedIn ):

... prinsip desain inti dari metode statis, prinsip yang memberi mereka nama mereka ... [adalah] ... itu selalu dapat ditentukan dengan tepat, pada waktu kompilasi, metode apa yang akan dipanggil. Artinya, metode ini dapat diselesaikan hanya dengan analisis statis kode.

Perhatikan bahwa Lippert tidak meninggalkan ruang untuk metode tipe yang disebut:

Yaitu, metode yang dikaitkan dengan suatu tipe (seperti statis), yang tidak mengambil argumen “ini” yang tidak dapat dibatalkan (tidak seperti instance atau virtual), tetapi metode yang dipanggil akan bergantung pada tipe T yang dikonstruksi ( tidak seperti statis, yang harus ditentukan pada waktu kompilasi).

tetapi belum diyakinkan tentang manfaatnya.

Mark Brackett
sumber
5
Luar biasa, ini adalah jawaban yang ingin saya tulis - saya hanya tidak tahu detail implementasi.
Chris Marasti-Georg
5
Jawaban yang bagus Dan saya ingin 'metode tipe' ini! Akan sangat berguna dalam banyak kesempatan (pikirkan tentang metadata untuk tipe / kelas).
Philip Daubmeier
23
Ini jawaban yang benar. Anda memberikan antarmuka kepada seseorang, mereka perlu tahu cara memanggil metode. Antarmuka hanyalah tabel metode virtual . Kelas statis Anda tidak memilikinya. Penelepon tidak akan tahu cara memanggil metode. (Sebelum saya membaca jawaban ini saya pikir C # hanya menjadi bertele-tele. Sekarang saya menyadari itu keterbatasan teknis, yang dikenakan oleh apa interface adalah ). Orang lain akan berbicara kepada Anda tentang bagaimana itu desain yang buruk. Ini bukan desain yang buruk - ini adalah batasan teknis.
Ian Boyd
2
Memberi +1 untuk benar-benar menjawab pertanyaan dan tidak bersikap angkuh dan cerewet. Seperti yang dikatakan Ian Boyd: "Ini bukan desain yang buruk - ini adalah batasan teknis."
Joshua Pech
3
Sangat mungkin untuk menghasilkan objek untuk kelas statis dengan vtable terkait. Lihatlah bagaimana Scala menangani objectdan bagaimana mereka diizinkan untuk mengimplementasikan antarmuka.
Sebastian Graf
97

Sebagian besar jawaban di sini sepertinya tidak ada artinya. Polimorfisme dapat digunakan tidak hanya antar instance, tetapi juga antar tipe. Ini sering dibutuhkan, ketika kita menggunakan obat generik.

Misalkan kita memiliki tipe parameter dalam metode generik dan kita perlu melakukan beberapa operasi dengannya. Kami tidak ingin melunakkan, karena kami tidak mengetahui konstruktor.

Sebagai contoh:

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>()

Sayangnya, saya hanya dapat menemukan alternatif "jelek":

  • Gunakan refleksi Jelek dan mengalahkan gagasan antarmuka dan polimorfisme.

  • Buat kelas pabrik yang benar-benar terpisah

    Ini mungkin sangat meningkatkan kompleksitas kode. Misalnya, jika kita mencoba memodelkan objek domain, setiap objek akan membutuhkan kelas repositori lain.

  • Instantiate dan panggil metode antarmuka yang diinginkan

    Ini bisa sulit untuk diimplementasikan bahkan jika kita mengendalikan sumber untuk kelas, digunakan sebagai parameter umum. Alasannya adalah, sebagai contoh, kita mungkin membutuhkan instance hanya dalam kondisi "terhubung ke DB" yang terkenal.

Contoh:

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

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

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

untuk menggunakan instantination untuk menyelesaikan masalah antarmuka statis kita perlu melakukan hal-hal berikut:

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...
    }
  }
}

Ini jelas jelek dan juga tidak perlu mempersulit kode untuk semua metode lain. Jelas, bukan solusi yang elegan!

Ivan Arjentinski
sumber
35
+1 untuk "Sebagian besar jawaban di sini sepertinya melewatkan seluruh inti masalahnya."; itu luar biasa bagaimana tampaknya hampir semua jawaban menghindari inti pertanyaan untuk menyelidiki sebagian besar kata-kata yang tidak berguna ...
5
@ Chris Berikut adalah contoh spesifik yang membuat saya baru saja mencapai batasan ini lagi. Saya ingin menambahkan antarmuka IResettable ke kelas untuk menunjukkan bahwa mereka men-cache data tertentu dalam variabel statis yang dapat diatur ulang oleh admin situs (misalnya daftar kategori pesanan, satu set tarif PPN, daftar kategori yang diambil dari API eksternal) untuk mengurangi DB dan hit API eksternal, ini jelas akan menggunakan metode reset statis. Ini memungkinkan saya untuk hanya mengotomatiskan deteksi kelas mana yang dapat diatur ulang. Saya masih bisa melakukan ini, tetapi metode ini tidak ditegakkan atau secara otomatis ditambahkan dalam IDE dan bergantung pada harapan.
mattmanser
6
@ Chris aku tidak setuju, pembunuhan besar-besaran. Itu selalu merupakan tanda cacat bahasa ketika lebih banyak arsitektur adalah solusi 'terbaik'. Ingat semua pola yang tidak ada yang membicarakan lagi karena C # mendapatkan obat generik dan anonim?
mattmanser
3
Tidak bisakah Anda menggunakan "di mana T: IQueryable, T: IDoSomeStaticMath" atau serupa?
Roger Willcocks
2
@ ChrisMarasti-Georg: Cara yang agak kotor tapi menarik adalah konstruksi kecil ini:, public abstract class DBObject<T> where T : DBObject<T>, new()dan kemudian buat semua kelas DB diwarisi sebagai DBObject<T>. Kemudian Anda dapat membuat fungsi mengambil-dengan-kunci fungsi statis dengan tipe kembali T dalam superclass abstrak, membuat fungsi tersebut membuat objek baru T, dan kemudian memanggil dilindungi String GetRetrieveByKeyQuery()(didefinisikan sebagai abstrak pada superclass) pada objek itu untuk mendapatkan permintaan aktual untuk dieksekusi. Meskipun ini mungkin mendapatkan sedikit topik
Nyerguds
20

Saya tahu ini pertanyaan lama, tapi menarik. Contohnya bukan yang terbaik. Saya pikir akan jauh lebih jelas jika Anda menunjukkan kasus penggunaan:

string DoSomething <T> () di mana T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

Hanya dengan memiliki metode statis mengimplementasikan sebuah antarmuka tidak akan mencapai apa yang Anda inginkan; apa yang diperlukan adalah memiliki anggota statis sebagai bagian dari antarmuka. Saya pasti bisa membayangkan banyak kasus penggunaan untuk itu, terutama ketika datang untuk dapat membuat sesuatu. Dua pendekatan yang bisa saya tawarkan yang mungkin bisa membantu:

  1. Buat kelas generik statis yang parameter tipenya akan menjadi tipe yang akan Anda sampaikan ke DoSomething di atas. Setiap variasi dari kelas ini akan memiliki satu atau lebih anggota statis yang memegang barang yang terkait dengan jenis itu. Informasi ini dapat diberikan baik dengan meminta setiap kelas yang diminati memanggil rutin "mendaftar informasi", atau dengan menggunakan Refleksi untuk mendapatkan informasi ketika konstruktor statis variasi kelas dijalankan. Saya percaya pendekatan yang terakhir digunakan oleh hal-hal seperti Comparer <T> .Default ().
  2. Untuk setiap kelas T yang diminati, tentukan kelas atau struct yang mengimplementasikan IGetWhthingClassInfo <T> dan memenuhi batasan "baru". Kelas tidak akan benar-benar berisi bidang apa pun, tetapi akan memiliki properti statis yang mengembalikan bidang statis dengan informasi tipe. Berikan tipe kelas itu atau struct ke rutin umum yang bersangkutan, yang akan dapat membuat instance dan menggunakannya untuk mendapatkan informasi tentang kelas lain. Jika Anda menggunakan kelas untuk tujuan ini, Anda mungkin harus mendefinisikan kelas generik statis seperti yang ditunjukkan di atas, untuk menghindari keharusan membuat instance objek-deskriptor baru setiap kali. Jika Anda menggunakan struct, biaya instantiation harus nihil, tetapi setiap tipe struct yang berbeda memerlukan ekspansi rutin DoSomething yang berbeda.

Tidak satu pun dari pendekatan ini yang benar-benar menarik. Di sisi lain, saya akan berharap bahwa jika mekanisme ada di CLR untuk menyediakan fungsionalitas semacam ini dengan bersih, .net akan memungkinkan seseorang untuk menentukan batasan parameter "baru" (karena mengetahui jika kelas memiliki konstruktor dengan tanda tangan tertentu akan tampak menjadi sebanding dalam kesulitan untuk mengetahui apakah ia memiliki metode statis dengan tanda tangan tertentu).

supercat
sumber
16

Saya berpandangan pendek.

Ketika awalnya dirancang, antarmuka hanya dimaksudkan untuk digunakan dengan instance kelas

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

Itu hanya dengan pengenalan antarmuka sebagai kendala untuk generik tidak menambahkan metode statis ke antarmuka memiliki penggunaan praktis.

(menanggapi komentar :) Saya percaya mengubahnya sekarang akan membutuhkan perubahan pada CLR, yang akan menyebabkan ketidakcocokan dengan majelis yang ada.

James Curran
sumber
Dalam konteks obat generik itulah saya pertama kali menemui masalah, tetapi saya ingin tahu apakah memasukkan metode statis di antarmuka dapat bermanfaat dalam konteks lain juga? Apakah ada alasan mengapa hal-hal tidak dapat diubah?
Kramii
saya juga menemukan ini ketika menerapkan kelas generik yang memerlukan tipe parameter untuk membuat dirinya sendiri dengan beberapa parameter. Karena baru () tidak dapat mengambil. Apakah Anda sudah tahu bagaimana melakukan ini, Kramii?
Tom
1
@ Kramii: Kontrak untuk API statis. Saya tidak ingin contoh objek, hanya jaminan tanda tangan tertentu, misalnya. IMatrixMultiplier atau ICustomSerializer. Fungsi / Tindakan / Delegasi sebagai anggota kelas melakukan trik, tetapi IMO ini kadang-kadang tampak seperti berlebihan, dan dapat membingungkan bagi yang tidak berpengalaman mencoba untuk memperpanjang API.
David Cuccia
14

Antarmuka menentukan perilaku suatu objek.

Metode statis tidak menentukan perilaku suatu objek, tetapi perilaku yang mempengaruhi suatu objek dalam beberapa cara.

John Kraft
sumber
71
Maaf .. saya tidak yakin itu benar! Antarmuka tidak menentukan perilaku. Antarmuka mendefinisikan serangkaian operasi bernama. Dua kelas dapat mengimplementasikan metode antarmuka untuk berperilaku dengan cara yang sangat berbeda. Jadi, antarmuka tidak menentukan perilaku sama sekali. Kelas yang mengimplementasikannya.
Scott Langham
1
Semoga Anda tidak berpikir saya pilih-pilih .. tapi saya pikir itu perbedaan penting bagi siapa pun yang belajar OO untuk mengerti.
Scott Langham
4
Antarmuka seharusnya menentukan kontrak yang mencakup perilaku dan presentasi. Itu sebabnya mengubah perilaku panggilan antarmuka adalah tidak-tidak karena keduanya seharusnya diperbaiki. Jika Anda memiliki antarmuka di mana panggilan bertindak berbeda (yaitu IList. Tambah melakukan penghapusan) itu tidak akan benar.
Jeff Yates
14
Nah, ya, Anda akan keliru dalam menentukan metode untuk bertingkah laku tidak sesuai namanya. Jika Anda memiliki IAlertService.GetAssistance (), perilakunya bisa berupa lampu kilat, membunyikan alarm, atau menyodok mata dengan tongkat.
Scott Langham
1
Suatu implementasi juga akan dapat menulis ke file log. Perilaku ini tidak ditentukan dalam antarmuka. Tapi, mungkin Anda benar. Perilaku untuk 'mendapatkan bantuan' harus benar-benar dihormati.
Scott Langham
14

Sejauh antarmuka mewakili "kontrak", tampaknya cukup masuk akal bagi kelas statis untuk mengimplementasikan antarmuka.

Argumen di atas semua sepertinya melewatkan poin ini tentang kontrak.

George
sumber
2
Saya setuju sepenuhnya dengan jawaban sederhana namun efektif ini. Apa yang akan menarik dalam "antarmuka statis" adalah bahwa itu akan mewakili kontrak. Mungkin seharusnya tidak disebut "antarmuka statis" tetapi kami masih kehilangan sebuah konstruk. Misalnya, periksa .NET's official doc tentang antarmuka ICustomMarshaler. Ini membutuhkan kelas yang mengimplementasikannya untuk "menambahkan metode statis yang disebut GetInstance yang menerima sebuah String sebagai parameter dan memiliki tipe pengembalian ICustomMarshaler". Itu benar-benar terlihat seperti definisi "antarmuka statis" dalam bahasa Inggris, sementara saya lebih suka di C # ...
Simon Mourier
@SimonMourier Dokumentasi itu bisa ditulis lebih jelas, tetapi Anda salah mengartikannya. Bukan antarmuka ICustomMarshaler yang membutuhkan metode statis GetInstance. Atribut kode [MarshalAs] yang mengharuskan ini. Mereka menggunakan pola pabrik untuk memungkinkan atribut mendapatkan instance dari marshaler terlampir. Sayangnya mereka benar-benar lupa memasukkan dokumentasi persyaratan GetInstance pada halaman dokumentasi MarshalAs (hanya menunjukkan contoh menggunakan implementasi marshaling bawaan).
Scott Gartner
@ScottGartner - Jangan mengerti maksud Anda. msdn.microsoft.com/en-us/library/… dengan jelas menyatakan: "Selain menerapkan antarmuka ICustomMarshaler, marshaler khusus harus menerapkan metode statis yang disebut GetInstance yang menerima String sebagai parameter dan memiliki tipe pengembalian ICustomMarshaler. Ini metode statis disebut oleh lapisan interop COM runtime bahasa umum untuk membuat instance dari marshaler kustom. " Ini jelas merupakan definisi kontrak statis.
Simon Mourier
9

Karena tujuan dari suatu antarmuka adalah untuk memungkinkan polimorfisme, dapat melewati instance dari sejumlah kelas tertentu yang semuanya telah didefinisikan untuk mengimplementasikan antarmuka yang ditentukan ... menjamin bahwa dalam panggilan polimorfik Anda, kode akan dapat menemukan metode yang Anda panggil. tidak masuk akal untuk memungkinkan metode statis untuk mengimplementasikan antarmuka,

Bagaimana Anda menyebutnya ??


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;  
Charles Bretana
sumber
1
Bahasa lain (Java, misalnya) memungkinkan metode statis dipanggil dari instance objek, meskipun Anda akan mendapat peringatan bahwa Anda harus memanggil mereka dari konteks statis.
Chris Marasti-Georg
Mengizinkan metode statis untuk dipanggil dari instance diperbolehkan di. Net juga. Itu hal yang berbeda. Jika metode statis menerapkan antarmuka, Anda bisa menyebutnya TANPA instance. ITULAH yang tidak masuk akal. Ingat Anda tidak bisa meletakkan implementasi di antarmuka, itu harus di dalam kelas. Jadi jika lima kelas berbeda didefinisikan untuk mengimplementasikan antarmuka itu, dan masing-masing memiliki implementasi yang berbeda dari metode statis ini, yang mana yang akan digunakan oleh kompiler?
Charles Bretana
1
@CharlesBretana dalam kasus obat generik, yang untuk jenis yang diteruskan. Saya melihat banyak kegunaan memiliki antarmuka untuk metode statis (atau jika Anda mau, sebut saja "antarmuka statis" dan memungkinkan kelas untuk mendefinisikan kedua antarmuka statis dan antarmuka contoh). Jadi jika saya punya Whatever<T>() where T:IMyStaticInterfacesaya bisa memanggil Whatever<MyClass>()dan ada T.MyStaticMethod()di dalam Whatever<T>()implementasi tanpa perlu contoh. Metode untuk memanggil akan ditentukan dalam runtime. Anda dapat melakukan ini melalui refleksi, tetapi tidak ada "kontrak" yang harus dipaksakan.
Jcl
4

Mengenai metode statis yang digunakan dalam konteks non-generik, saya setuju bahwa tidak masuk akal untuk memperbolehkannya dalam antarmuka, karena Anda tidak akan dapat memanggil mereka jika Anda memiliki referensi ke antarmuka. Namun ada lubang mendasar dalam desain bahasa yang dibuat dengan menggunakan antarmuka BUKAN dalam konteks polimorfik, tetapi dalam konteks generik. Dalam hal ini antarmuka bukanlah antarmuka sama sekali tetapi merupakan kendala. Karena C # tidak memiliki konsep batasan di luar antarmuka, maka tidak ada fungsi substansial. Inti masalah:

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

Di sini tidak ada polimorfisme, generik menggunakan tipe objek yang sebenarnya dan memanggil operator + =, tetapi ini gagal karena ia tidak dapat mengatakan dengan pasti bahwa operator itu ada. Solusi sederhana adalah menentukannya dalam batasan; solusi sederhana tidak mungkin karena operator statis dan metode statis tidak dapat berada dalam suatu antarmuka dan (inilah masalahnya) kendala direpresentasikan sebagai antarmuka.

Apa yang dibutuhkan C # adalah tipe kendala nyata, semua antarmuka juga akan menjadi kendala, tetapi tidak semua kendala akan menjadi antarmuka maka Anda bisa melakukan ini:

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;
    }
}

Sudah banyak pembicaraan tentang membuat IArithmetic untuk semua tipe numerik untuk diimplementasikan, tetapi ada kekhawatiran tentang efisiensi, karena kendala bukan konstruk polimorfik, membuat kendala CArithmetic akan menyelesaikan masalah itu.

Jeremy Sorensen
sumber
Operator di C # bersifat statis, jadi ini masih tidak masuk akal.
John Saunders
Itulah intinya, kendala memverifikasi bahwa TYPE (bukan instance) memiliki operator + (dan dengan ekstensi + = operator) dan memungkinkan templat untuk dihasilkan, maka ketika substitusi templat aktual terjadi, objek yang digunakan dijamin akan tipe yang memiliki operator itu (atau metode statis lainnya.) Jadi Anda dapat mengatakan: SumElements <int> (0, 5); yang akan instantiate ini: SumElements (int initVal, int [] values) {foreach (var v in values) {initVal + = v; }}, yang tentu saja, sangat masuk akal
Jeremy Sorensen
1
Ingat itu bukan templat. Ini generik, yang tidak sama dengan template C ++.
John Saunders
@ JohnSaunders: Generik bukan templat, dan saya tidak tahu bagaimana mereka dapat dibuat secara wajar untuk bekerja dengan operator yang terikat secara statis, tetapi dalam konteks umum ada banyak kasus di mana dapat berguna untuk menentukan bahwa Tharus memiliki statis anggota (mis. pabrik yang memproduksi mesin Tvirtual). Bahkan tanpa perubahan runtime, saya pikir akan mungkin untuk menentukan konvensi dan metode pembantu yang akan memungkinkan bahasa untuk menerapkan hal seperti gula sintaksis secara efisien dan interoperable. Jika untuk setiap antarmuka dengan metode statis virtual ada kelas pembantu ...
supercat
1
@ JohnSaunders: Masalahnya bukan pada metode mengimplementasikan antarmuka, tetapi kompiler tidak dapat memilih metode virtual untuk memanggil tanpa memiliki instance objek yang menjadi dasar pemilihan. Solusinya adalah dengan mengirim panggilan "antarmuka statis" yang tidak menggunakan pengiriman virtual (yang tidak akan berfungsi) melainkan menggunakan panggilan ke kelas statis umum. Pengiriman umum berbasis tipe tidak harus memiliki instance, tetapi hanya memiliki tipe.
supercat
3

Karena antarmuka berada dalam struktur pewarisan, dan metode statis tidak mewarisi dengan baik.

Joel Coehoorn
sumber
3

Apa yang tampaknya Anda inginkan akan memungkinkan metode statis dipanggil melalui Tipe atau instance dari tipe itu. Paling tidak ini akan menghasilkan ambiguitas yang bukan sifat yang diinginkan.

Akan ada perdebatan tanpa akhir tentang apakah itu penting, yang merupakan praktik terbaik dan apakah ada masalah kinerja yang melakukannya dengan satu atau lain cara. Dengan tidak mendukungnya, C # menyelamatkan kita karena harus mengkhawatirkannya.

Kemungkinan juga bahwa penyusun yang sesuai dengan keinginan ini akan kehilangan beberapa optimisasi yang mungkin datang dengan pemisahan yang lebih ketat antara metode instance dan metode statis.

AnthonyWJones
sumber
Menariknya, sangat mungkin untuk memanggil metode statis dengan kedua Jenis Iklan Instans di VB.Net (meskipun IDE memberikan peringatan dalam kasus terakhir). Tampaknya tidak menjadi masalah. Anda mungkin benar tentang optimisasi.
Kramii
3

Anda dapat menganggap metode statis dan metode non-statis suatu kelas sebagai antarmuka yang berbeda. Ketika dipanggil, metode statis menyelesaikan ke objek kelas statis tunggal, dan metode non-statis menyelesaikan contoh kelas yang Anda hadapi. Jadi, jika Anda menggunakan metode statis dan non-statis dalam sebuah antarmuka, Anda akan secara efektif mendeklarasikan dua antarmuka ketika kita ingin antarmuka digunakan untuk mengakses satu hal yang kohesif.

Scott Langham
sumber
Ini adalah POV yang menarik, dan mungkin salah satu yang ada di pikiran para desainer C #. Saya akan memikirkan anggota statis dengan cara yang berbeda dari sekarang.
Kramii
3

Untuk memberikan contoh di mana saya kehilangan implementasi statis dari metode antarmuka atau apa yang diperkenalkan Mark Brackett sebagai "apa yang disebut metode metode":

Saat membaca dari penyimpanan database, kami memiliki kelas DataTable generik yang menangani pembacaan dari tabel struktur apa pun. Semua informasi spesifik tabel dimasukkan ke dalam satu kelas per tabel yang juga menyimpan data untuk satu baris dari DB dan yang harus mengimplementasikan antarmuka IDataRow. Termasuk dalam IDataRow adalah deskripsi struktur tabel untuk dibaca dari database. DataTable harus meminta datastructure dari IDataRow sebelum membaca dari DB. Saat ini terlihat seperti:

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 hanya diperlukan satu kali untuk setiap tabel untuk dibaca, overhead untuk instantiating satu lagi contoh minimal. Namun, akan lebih baik dalam hal ini di sini.


sumber
1

FYI: Anda bisa mendapatkan perilaku serupa dengan apa yang Anda inginkan dengan membuat metode ekstensi untuk antarmuka. Metode ekstensi akan menjadi perilaku statis yang dibagi dan tidak dapat ditimpa. Namun, sayangnya, metode statis ini tidak akan menjadi bagian dari kontrak.

Daniel Auger
sumber
1

Antarmuka adalah set abstrak dari fungsionalitas yang tersedia yang tersedia.

Apakah atau tidak metode di antarmuka itu bertindak sebagai statis atau tidak adalah detail implementasi yang harus disembunyikan di balik antarmuka . Akan salah untuk mendefinisikan metode antarmuka sebagai statis karena Anda tidak perlu memaksakan metode yang akan diterapkan dengan cara tertentu.

Jika metode didefinisikan sebagai statis, kelas yang mengimplementasikan antarmuka tidak akan dienkapsulasi sebagaimana mestinya. Enkapsulasi adalah hal yang baik untuk diperjuangkan dalam desain berorientasi objek (saya tidak akan membahas mengapa, Anda dapat membacanya di sini: http://en.wikipedia.org/wiki/Object-oriented ). Karena alasan ini, metode statis tidak diizinkan di antarmuka.

Scott Langham
sumber
1
Ya, statis dalam deklarasi antarmuka akan konyol. Kelas tidak boleh dipaksa untuk mengimplementasikan antarmuka dengan cara tertentu. Namun, apakah C # membatasi kelas untuk mengimplementasikan antarmuka menggunakan metode non-statis. Apakah ini masuk akal? Mengapa?
Kramii
Anda tidak dapat menggunakan kata kunci 'statis'. Tidak ada batasan karena Anda tidak perlu kata kunci statis untuk menulis metode yang berperilaku statis. Cukup tulis metode yang tidak mengakses anggota objeknya, maka ia akan berperilaku seperti metode statis. Jadi ada batasan.
Scott Langham
Benar Scott, tetapi itu tidak memungkinkan seseorang untuk mengakses metode itu secara statis, di bagian lain dari kode. Bukannya saya bisa memikirkan alasan yang Anda inginkan, tapi sepertinya itulah yang diminta OP.
Chris Marasti-Georg
Nah, jika Anda benar-benar merasa perlu, Anda dapat menuliskannya sebagai metode statis untuk mengakses bagian lain dari kode, dan cukup tulis metode non-statis untuk memanggil metode statis. Saya bisa saja salah, tetapi saya ragu itu adalah tujuan si penanya.
Scott Langham
1

Kelas statis harus dapat melakukan ini sehingga dapat digunakan secara umum. Saya malah harus menerapkan Singleton untuk mencapai hasil yang diinginkan.

Saya memiliki banyak kelas Lapisan Bisnis Statis yang menerapkan metode CRUD seperti "Buat", "Baca", "Perbarui", "Hapus" untuk setiap jenis entitas seperti "Pengguna", "Tim", dll .. Lalu saya membuat basis kontrol yang memiliki properti abstrak untuk kelas Business Layer yang menerapkan metode CRUD. Ini memungkinkan saya untuk mengotomatiskan operasi "Buat", "Baca", "Perbarui", "Hapus" dari kelas dasar. Saya harus menggunakan Singleton karena keterbatasan statis.

Louis Rebolloso
sumber
1

Kebanyakan orang sepertinya lupa bahwa dalam OOP Classes adalah objek juga, dan karena itu mereka memiliki pesan, yang karena alasan tertentu c # memanggil "metode statis". Fakta bahwa ada perbedaan antara objek instan dan objek kelas hanya menunjukkan kekurangan atau kekurangan dalam bahasa. Optimis tentang c # ...

Mar Bar
sumber
2
Masalahnya adalah bahwa pertanyaan ini bukan tentang "di OOP". Ini tentang "dalam C #". Di C #, tidak ada "pesan".
John Saunders
1

OK di sini adalah contoh membutuhkan 'metode tipe'. Saya membuat salah satu dari sekumpulan kelas berdasarkan beberapa sumber XML. Jadi saya punya

  static public bool IsHandled(XElement xml)

fungsi yang disebut pada gilirannya di setiap kelas.

Fungsi ini harus statis karena kalau tidak, kita membuang-buang waktu membuat objek yang tidak pantas. Seperti yang ditunjukkan @Ian Boyde, ini bisa dilakukan di kelas pabrik, tetapi ini hanya menambah kompleksitas.

Akan lebih baik menambahkannya ke antarmuka untuk memaksa implementor kelas mengimplementasikannya. Ini tidak akan menyebabkan overhead yang signifikan - ini hanya pemeriksaan waktu kompilasi / tautan dan tidak mempengaruhi vtable.

Namun, itu juga akan menjadi peningkatan yang cukup kecil. Karena metode ini statis, saya sebagai penelepon, harus memanggilnya secara eksplisit sehingga mendapatkan kompilasi kesalahan segera jika tidak diterapkan. Mengizinkan untuk ditentukan pada antarmuka akan berarti kesalahan ini muncul sedikit lebih awal dalam siklus pengembangan, tetapi ini sepele dibandingkan dengan masalah antarmuka rusak lainnya.

Jadi ini adalah fitur potensial kecil yang mungkin sebaiknya tidak diseimbangkan.

Stephen Westlake
sumber
1

Fakta bahwa kelas statis diimplementasikan dalam C # oleh Microsoft menciptakan contoh khusus dari kelas dengan elemen statis hanyalah keanehan bagaimana fungsionalitas statis dicapai. Ini bukan poin teoretis.

Antarmuka HARUS menjadi deskriptor antarmuka kelas - atau bagaimana berinteraksi dengan, dan itu harus mencakup interaksi yang statis. Definisi umum antarmuka (dari Meriam-Webster): tempat atau area tempat berbagai hal saling bertemu dan berkomunikasi atau saling memengaruhi. Ketika Anda menghilangkan komponen statis dari kelas atau kelas statis sama sekali, kami mengabaikan sebagian besar bagaimana anak-anak nakal ini berinteraksi.

Berikut ini adalah contoh yang sangat jelas tentang di mana bisa menggunakan antarmuka dengan kelas statis akan sangat berguna:

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

Saat ini, saya menulis kelas statis yang berisi metode ini tanpa memeriksa untuk memastikan bahwa saya belum melupakan apa pun. Seperti masa lalu pemrograman yang buruk sebelum OOP.

Thomas Phaneuf
sumber
Ini adalah pertanyaan kuno; hari ini, kami berusaha sangat keras untuk menghindari pertanyaan berdasarkan opini karena mereka sulit dijawab secara otoritatif. Satu-satunya cara yang baik untuk menjawab ini adalah dengan menjelaskan alasan MS, atau yang seharusnya ditunjukkan, untuk melakukannya dengan cara yang mereka lakukan.
Nathan Tuggy
1

C # dan CLR harus mendukung metode statis di antarmuka seperti halnya Java. Pengubah statis adalah bagian dari definisi kontrak dan memang memiliki makna, khususnya bahwa perilaku dan nilai kembali tidak bervariasi berdasarkan pada contoh meskipun mungkin masih berbeda dari panggilan ke panggilan.

Yang mengatakan, saya merekomendasikan bahwa ketika Anda ingin menggunakan metode statis dalam suatu antarmuka dan tidak bisa, gunakan anotasi sebagai gantinya. Anda akan mendapatkan fungsionalitas yang Anda cari.

Daniel Barbalace
sumber
0

Saya pikir jawaban singkatnya adalah "karena tidak ada gunanya". Untuk memanggil metode antarmuka, Anda memerlukan turunan tipe. Dari metode instan Anda dapat memanggil metode statis apa pun yang Anda inginkan.

Mackenir
sumber
0

Saya pikir pertanyaannya adalah pada kenyataan bahwa C # membutuhkan kata kunci lain, untuk situasi seperti ini. Anda menginginkan metode yang nilai pengembaliannya hanya bergantung pada jenis yang dipanggil. Anda tidak dapat menyebutnya "statis" jika jenis kata tidak dikenal. Tetapi begitu jenisnya diketahui, itu akan menjadi statis. "Statis yang belum terselesaikan" adalah idenya - ini belum statis, tetapi begitu kita mengetahui tipe penerima, itu akan menjadi. Ini adalah konsep yang sangat baik, itulah sebabnya programmer terus memintanya. Tapi itu tidak cukup cocok dengan cara para perancang berpikir tentang bahasa.

Karena tidak tersedia, saya menggunakan metode non-statis dengan cara yang ditunjukkan di bawah ini. Tidak terlalu ideal, tetapi saya tidak bisa melihat pendekatan yang lebih masuk akal, setidaknya tidak untuk saya.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}
William Jockusch
sumber
0

Sesuai dengan konsep berorientasi objek, antarmuka diimplementasikan oleh kelas dan memiliki kontrak untuk mengakses fungsi yang diimplementasikan ini (atau metode) menggunakan objek.

Jadi jika Anda ingin mengakses metode Kontrak Antarmuka Anda harus membuat objek. Itu selalu harus yang tidak diperbolehkan dalam hal metode statis. Kelas statis, metode, dan variabel tidak pernah memerlukan objek dan memuat dalam memori tanpa membuat objek dari area tersebut (atau kelas) atau Anda dapat mengatakan tidak memerlukan Pembuatan Objek.

Sanjeev Saraswat
sumber
0

Secara konseptual tidak ada alasan mengapa antarmuka tidak dapat mendefinisikan kontrak yang mencakup metode statis.

Untuk implementasi bahasa C # saat ini, pembatasan ini disebabkan oleh tunjangan warisan kelas dasar dan antarmuka. Jika "class SomeBaseClass" mengimplementasikan "interface ISomeInterface" dan "class SomeDerivedClass: SomeBaseClass, ISomeInterface" juga mengimplementasikan antarmuka, metode statis untuk mengimplementasikan metode antarmuka akan gagal dikompilasi karena metode statis tidak dapat memiliki tanda tangan yang sama dengan metode instance (yang akan hadir di kelas dasar untuk mengimplementasikan antarmuka).

Kelas statis secara fungsional identik dengan singleton dan melayani tujuan yang sama dengan singleton dengan sintaks yang lebih bersih. Karena singleton dapat mengimplementasikan antarmuka, implementasi antarmuka oleh statika secara konseptual valid.

Jadi itu hanya bermuara pada batasan konflik nama C # misalnya dan metode statis dari nama yang sama di seluruh warisan. Tidak ada alasan mengapa C # tidak dapat "ditingkatkan" untuk mendukung kontrak metode statis (antarmuka).

Greg McPherran
sumber
-1

Ketika sebuah kelas mengimplementasikan antarmuka, itu menciptakan instance untuk anggota antarmuka. Sementara tipe statis tidak memiliki instance, tidak ada gunanya memiliki tanda tangan statis di antarmuka.

Vinay Chanumolu
sumber