Warum wurde C # so entworfen?
So wie ich es verstehe, beschreibt eine Schnittstelle nur das Verhalten und dient dem Zweck, eine vertragliche Verpflichtung für Klassen zu beschreiben, die die Schnittstelle implementieren, dass ein bestimmtes Verhalten implementiert wird.
Wenn Klassen dieses Verhalten in einer gemeinsamen Methode implementieren möchten, warum sollten sie es nicht tun?
Hier ist ein Beispiel für das, was ich vorhabe:
// 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;
}
....
}
c#
oop
language-features
Kramii
quelle
quelle
IListItem.ScreenName() => ScreenName()
(unter Verwendung der C # 7-Syntax) implementiert die Schnittstellenmethode explizit durch Aufrufen der statischen Methode. Die Dinge werden jedoch hässlich, wenn Sie die Vererbung hinzufügen (Sie müssen die Schnittstelle neu implementieren)Antworten:
Angenommen, Sie fragen, warum Sie dies nicht tun können:
Das macht für mich semantisch keinen Sinn. Auf einer Schnittstelle angegebene Methoden sollten vorhanden sein, um den Vertrag für die Interaktion mit einem Objekt anzugeben. Mit statischen Methoden können Sie nicht mit einem Objekt interagieren. Wenn Sie sich in der Position befinden, in der Ihre Implementierung statisch gemacht werden könnte, müssen Sie sich möglicherweise fragen, ob diese Methode wirklich zur Schnittstelle gehört.
Um Ihr Beispiel zu implementieren, würde ich Animal eine const-Eigenschaft geben, mit der weiterhin über einen statischen Kontext darauf zugegriffen werden kann, und diesen Wert in der Implementierung zurückgeben.
In einer komplizierteren Situation können Sie jederzeit eine andere statische Methode deklarieren und an diese delegieren. Bei dem Versuch, ein Beispiel zu finden, konnte ich mir keinen Grund vorstellen, warum Sie etwas nicht Triviales sowohl im statischen als auch im Instanzkontext tun würden. Deshalb erspare ich Ihnen einen FooBar-Blob und nehme ihn als Hinweis darauf, dass dies möglich ist keine gute Idee sein.
quelle
public static object MethodName(this IBaseClass base)
innerhalb einer statischen Klasse. Der Nachteil ist jedoch, dass im Gegensatz zu einer Schnittstellenvererbung die einzelnen Erben nicht gezwungen werden, die Methodik gut zu überschreiben.Mein (vereinfachter) technischer Grund ist, dass statische Methoden nicht in der vtable enthalten sind und die Aufrufsite zur Kompilierungszeit ausgewählt wird. Es ist der gleiche Grund, warum Sie keine Override- oder virtuellen statischen Mitglieder haben können. Für weitere Details benötigen Sie einen CS-Absolventen oder einen Compiler-Wonk - von denen ich keiner bin.
Aus politischen Gründen zitiere ich Eric Lippert (der ein Compiler Wonk ist und einen Bachelor of Mathematics, Informatik und Angewandte Mathematik von der University of Waterloo besitzt (Quelle: LinkedIn ):
Beachten Sie, dass Lippert Platz für eine sogenannte Typmethode lässt:
ist aber noch von seiner Nützlichkeit zu überzeugen.
quelle
object
s umgeht und wie sie Schnittstellen implementieren dürfen.Die meisten Antworten hier scheinen den ganzen Punkt zu verfehlen. Polymorphismus kann nicht nur zwischen Instanzen, sondern auch zwischen Typen verwendet werden. Dies wird oft benötigt, wenn wir Generika verwenden.
Angenommen, wir haben einen Typparameter in der generischen Methode und müssen einige Operationen damit ausführen. Wir wollen nicht instanziieren, weil wir die Konstruktoren nicht kennen.
Zum Beispiel:
Leider kann ich mir nur "hässliche" Alternativen einfallen lassen:
Verwenden Sie Reflexion Hässlich und schlägt die Idee von Schnittstellen und Polymorphismus.
Erstellen Sie eine vollständig separate Factory-Klasse
Dies kann die Komplexität des Codes erheblich erhöhen. Wenn wir beispielsweise versuchen, Domänenobjekte zu modellieren, benötigt jedes Objekt eine andere Repository-Klasse.
Instanziieren Sie und rufen Sie dann die gewünschte Schnittstellenmethode auf
Dies kann schwierig zu implementieren sein, selbst wenn wir die Quelle für die Klassen steuern, die als generische Parameter verwendet werden. Der Grund dafür ist, dass sich die Instanzen möglicherweise nur im bekannten Status "Mit DB verbunden" befinden müssen.
Beispiel:
Um die Instanziierung zur Lösung des statischen Schnittstellenproblems zu verwenden, müssen wir Folgendes tun:
Dies ist offensichtlich hässlich und auch unnötig kompliziert den Code für alle anderen Methoden. Natürlich auch keine elegante Lösung!
quelle
public abstract class DBObject<T> where T : DBObject<T>, new()
, und dann alle DB-Klassen als erben lassenDBObject<T>
. Dann können Sie das Abrufen per Schlüssel zu einer statischen Funktion mit dem Rückgabetyp T in der abstrakten Oberklasse machen, diese Funktion ein neues Objekt von T erstellen lassen und dann ein geschütztesString GetRetrieveByKeyQuery()
(in der Oberklasse als abstrakt definiertes) Objekt für dieses Objekt aufrufen, um es abzurufen die tatsächlich auszuführende Abfrage. Obwohl dies ein bisschen vom ThemaIch weiß, dass es eine alte Frage ist, aber es ist interessant. Das Beispiel ist nicht das beste. Ich denke, es wäre viel klarer, wenn Sie einen Anwendungsfall zeigen würden:
Nur statische Methoden eine Schnittstelle implementieren zu lassen , würde nicht das erreichen, was Sie wollen. Was benötigt würde, wäre, statische Elemente als Teil einer Schnittstelle zu haben. Ich kann mir sicherlich viele Anwendungsfälle dafür vorstellen, besonders wenn es darum geht, Dinge erstellen zu können. Zwei Ansätze, die ich anbieten könnte und die hilfreich sein könnten:
Keiner dieser Ansätze ist wirklich ansprechend. Andererseits würde ich erwarten, dass, wenn die Mechanismen in CLR vorhanden wären, um diese Art von Funktionalität sauber bereitzustellen, .net es erlauben würde, parametrisierte "neue" Einschränkungen anzugeben (da zu wissen scheint, ob eine Klasse einen Konstruktor mit einer bestimmten Signatur hat in Schwierigkeiten vergleichbar sein mit dem Wissen, ob es eine statische Methode mit einer bestimmten Signatur gibt).
quelle
Kurzsichtigkeit, würde ich vermuten.
Bei der ursprünglichen Entwicklung sollten Schnittstellen nur für Klasseninstanzen verwendet werden
Erst mit der Einführung von Schnittstellen als Einschränkungen für Generika hatte das Hinzufügen einer statischen Methode zu einer Schnittstelle eine praktische Verwendung.
(Antwort auf Kommentar :) Ich glaube, eine Änderung würde jetzt eine Änderung der CLR erfordern, was zu Inkompatibilitäten mit vorhandenen Baugruppen führen würde.
quelle
Schnittstellen geben das Verhalten eines Objekts an.
Statische Methoden geben kein Verhalten eines Objekts an, sondern ein Verhalten, das ein Objekt in irgendeiner Weise beeinflusst.
quelle
In dem Maße, in dem Schnittstellen "Verträge" darstellen, erscheint es für statische Klassen ziemlich vernünftig, Schnittstellen zu implementieren.
Die obigen Argumente scheinen alle diesen Punkt über Verträge zu verfehlen.
quelle
Da der Zweck einer Schnittstelle darin besteht, Polymorphismus zuzulassen, kann eine Instanz einer beliebigen Anzahl definierter Klassen übergeben werden, die alle zur Implementierung der definierten Schnittstelle definiert wurden. Dies garantiert, dass der Code innerhalb Ihres polymorphen Aufrufs gefunden werden kann die Methode, die Sie aufrufen. Es macht keinen Sinn, eine statische Methode zur Implementierung der Schnittstelle zuzulassen.
Wie würdest du es nennen?
quelle
Whatever<T>() where T:IMyStaticInterface
habe, könnte ich anrufenWhatever<MyClass>()
undT.MyStaticMethod()
in derWhatever<T>()
Implementierung haben, ohne eine Instanz zu benötigen. Die aufzurufende Methode wird zur Laufzeit festgelegt. Sie können dies durch Reflexion tun, aber es gibt keinen "Vertrag", der erzwungen werden muss.In Bezug auf statische Methoden, die in nicht generischen Kontexten verwendet werden, stimme ich zu, dass es wenig sinnvoll ist, sie in Schnittstellen zuzulassen, da Sie sie nicht aufrufen könnten, wenn Sie ohnehin einen Verweis auf die Schnittstelle hätten. Es gibt jedoch eine grundlegende Lücke im Sprachdesign, die durch die Verwendung von Schnittstellen NICHT in einem polymorphen, sondern in einem generischen Kontext entsteht. In diesem Fall ist die Schnittstelle überhaupt keine Schnittstelle, sondern eine Einschränkung. Da C # kein Konzept für eine Einschränkung außerhalb einer Schnittstelle hat, fehlen wesentliche Funktionen. Ein typisches Beispiel:
Hier gibt es keinen Polymorphismus, das Generikum verwendet den tatsächlichen Typ des Objekts und ruft den Operator + = auf. Dies schlägt jedoch fehl, da nicht sicher gesagt werden kann, dass dieser Operator vorhanden ist. Die einfache Lösung besteht darin, sie in der Einschränkung anzugeben. Die einfache Lösung ist unmöglich, da Operatoren statisch sind und statische Methoden nicht in einer Schnittstelle enthalten sein können und (hier ist das Problem) Einschränkungen als Schnittstellen dargestellt werden.
Was C # benötigt, ist ein echter Einschränkungstyp. Alle Schnittstellen wären ebenfalls Einschränkungen, aber nicht alle Einschränkungen wären Schnittstellen. Dann könnten Sie Folgendes tun:
Es wurde bereits viel darüber gesprochen, eine IArithmetik für alle zu implementierenden numerischen Typen zu erstellen, aber es gibt Bedenken hinsichtlich der Effizienz, da eine Einschränkung kein polymorphes Konstrukt ist und eine CArithmetische Einschränkung dieses Problem lösen würde.
quelle
T
eine statische haben soll Mitglied (zB eine Fabrik, dieT
Instanzen produziert ). Selbst ohne Laufzeitänderungen denke ich, dass es möglich wäre, Konventionen und Hilfsmethoden zu definieren, mit denen Sprachen so etwas wie syntaktischen Zucker effizient und interoperabel implementieren können. Wenn es für jede Schnittstelle mit virtuellen statischen Methoden eineDa sich die Schnittstellen in der Vererbungsstruktur befinden und statische Methoden nicht gut erben.
quelle
Was Sie zu wollen scheinen, würde es ermöglichen, eine statische Methode sowohl über den Typ als auch über eine beliebige Instanz dieses Typs aufzurufen. Dies würde zumindest zu Mehrdeutigkeiten führen, die kein wünschenswertes Merkmal sind.
Es würde endlose Debatten darüber geben, ob es darauf ankommt, was die beste Vorgehensweise ist und ob es Leistungsprobleme gibt, die dies auf die eine oder andere Weise tun. Indem wir C # einfach nicht unterstützen, müssen wir uns keine Sorgen mehr machen.
Es ist auch wahrscheinlich, dass ein Compiler, der diesem Wunsch entspricht, einige Optimierungen verliert, die mit einer strengeren Trennung zwischen Instanz- und statischen Methoden einhergehen können.
quelle
Sie können sich die statischen und nicht statischen Methoden einer Klasse als unterschiedliche Schnittstellen vorstellen. Beim Aufruf werden statische Methoden in das statische Singleton-Klassenobjekt und nicht statische Methoden in die Instanz der Klasse aufgelöst, mit der Sie sich befassen. Wenn Sie also statische und nicht statische Methoden in einer Schnittstelle verwenden, deklarieren Sie effektiv zwei Schnittstellen, wenn wir wirklich möchten, dass Schnittstellen für den Zugriff auf eine zusammenhängende Sache verwendet werden.
quelle
Um ein Beispiel zu geben, bei dem mir entweder die statische Implementierung von Schnittstellenmethoden fehlt oder was Mark Brackett als "sogenannte Typmethode" eingeführt hat:
Beim Lesen aus einem Datenbankspeicher verfügen wir über eine generische DataTable-Klasse, die das Lesen aus einer Tabelle einer beliebigen Struktur übernimmt. Alle tabellenspezifischen Informationen werden in eine Klasse pro Tabelle gestellt, die auch Daten für eine Zeile aus der Datenbank enthält und eine IDataRow-Schnittstelle implementieren muss. In der IDataRow ist eine Beschreibung der Struktur der Tabelle enthalten, die aus der Datenbank gelesen werden soll. Die DataTable muss die Datenstruktur in der IDataRow anfordern, bevor sie aus der Datenbank gelesen werden kann. Derzeit sieht das so aus:
Die GetDataStructure ist nur einmal erforderlich, damit jede Tabelle gelesen werden kann. Der Aufwand für das Instanziieren einer weiteren Instanz ist minimal. In diesem Fall wäre es hier jedoch schön.
quelle
Zu Ihrer Information: Sie könnten ein ähnliches Verhalten wie gewünscht erzielen, indem Sie Erweiterungsmethoden für die Schnittstelle erstellen. Die Erweiterungsmethode wäre ein gemeinsames, nicht überschreibbares statisches Verhalten. Leider wäre diese statische Methode nicht Vertragsbestandteil.
quelle
Schnittstellen sind abstrakte Sätze definierter verfügbarer Funktionen.
Ob sich eine Methode in dieser Schnittstelle statisch verhält oder nicht, ist ein Implementierungsdetail, das hinter der Schnittstelle verborgen werden sollte . Es wäre falsch, eine Schnittstellenmethode als statisch zu definieren, da Sie die Implementierung der Methode auf eine bestimmte Weise unnötig erzwingen würden.
Wenn Methoden als statisch definiert würden, wäre die Klasse, die die Schnittstelle implementiert, nicht so gekapselt, wie sie sein könnte. Kapselung ist eine gute Sache, nach der man im objektorientierten Design streben sollte (ich werde nicht darauf eingehen, warum, das können Sie hier lesen: http://en.wikipedia.org/wiki/Object-oriented ). Aus diesem Grund sind statische Methoden in Schnittstellen nicht zulässig.
quelle
Statische Klassen sollten dies können, damit sie generisch verwendet werden können. Ich musste stattdessen einen Singleton implementieren, um die gewünschten Ergebnisse zu erzielen.
Ich hatte eine Reihe von statischen Business-Layer-Klassen, die CRUD-Methoden wie "Erstellen", "Lesen", "Aktualisieren", "Löschen" für jeden Entitätstyp wie "Benutzer", "Team" usw. implementierten. Dann erstellte ich eine Basis Steuerelement mit einer abstrakten Eigenschaft für die Business Layer-Klasse, die die CRUD-Methoden implementiert hat. Dadurch konnte ich die Operationen "Erstellen", "Lesen", "Aktualisieren" und "Löschen" aus der Basisklasse automatisieren. Ich musste wegen der statischen Einschränkung einen Singleton verwenden.
quelle
Die meisten Leute scheinen zu vergessen, dass es sich bei OOP-Klassen auch um Objekte handelt, und daher haben sie Nachrichten, die c # aus irgendeinem Grund "statische Methode" nennt. Die Tatsache, dass Unterschiede zwischen Instanzobjekten und Klassenobjekten bestehen, zeigt nur Fehler oder Mängel in der Sprache. Optimist über c # obwohl ...
quelle
OK, hier ist ein Beispiel für die Notwendigkeit einer 'Typmethode'. Ich erstelle eine aus einer Reihe von Klassen, die auf Quell-XML basieren. Also habe ich eine
Funktion, die wiederum für jede Klasse aufgerufen wird.
Die Funktion sollte statisch sein, da wir sonst Zeit damit verschwenden, unangemessene Objekte zu erstellen. Wie @Ian Boyde betont, könnte dies in einer Fabrikklasse erfolgen, dies erhöht jedoch nur die Komplexität.
Es wäre schön, es der Schnittstelle hinzuzufügen, um Klassenimplementierer zur Implementierung zu zwingen. Dies würde keinen signifikanten Overhead verursachen - es handelt sich lediglich um eine Überprüfung der Kompilierungs- / Verknüpfungszeit und hat keinen Einfluss auf die vtable.
Es wäre jedoch auch eine eher geringfügige Verbesserung. Da die Methode statisch ist, muss ich als Aufrufer sie explizit aufrufen und erhalte sofort einen Kompilierungsfehler, wenn sie nicht implementiert ist. Das Zulassen, dass es auf der Schnittstelle angegeben wird, würde bedeuten, dass dieser Fehler geringfügig früher im Entwicklungszyklus auftritt. Dies ist jedoch im Vergleich zu anderen Problemen mit defekten Schnittstellen trivial.
Es handelt sich also um ein geringfügiges potenzielles Merkmal, das unter dem Strich wahrscheinlich am besten weggelassen wird.
quelle
Die Tatsache, dass eine statische Klasse in C # von Microsoft implementiert wird, indem eine spezielle Instanz einer Klasse mit den statischen Elementen erstellt wird, ist nur eine Seltsamkeit, wie statische Funktionalität erreicht wird. Es ist kein theoretischer Punkt.
Eine Schnittstelle SOLLTE ein Deskriptor der Klassenschnittstelle sein - oder wie mit ihr interagiert wird, und dies sollte statische Interaktionen einschließen. Die allgemeine Definition der Schnittstelle (von Meriam-Webster): der Ort oder Bereich, an dem sich verschiedene Dinge treffen und miteinander kommunizieren oder sich gegenseitig beeinflussen. Wenn Sie statische Komponenten einer Klasse oder statische Klassen vollständig weglassen, ignorieren wir große Teile der Interaktion dieser bösen Jungs.
Hier ist ein sehr klares Beispiel dafür, wo die Verwendung von Schnittstellen mit statischen Klassen sehr nützlich wäre:
Derzeit schreibe ich die statischen Klassen, die diese Methoden enthalten, ohne zu überprüfen, ob ich nichts vergessen habe. Ist wie die schlechten alten Zeiten der Programmierung vor OOP.
quelle
C # und die CLR sollten wie Java statische Methoden in Schnittstellen unterstützen. Der statische Modifikator ist Teil einer Vertragsdefinition und hat eine Bedeutung, insbesondere, dass das Verhalten und der Rückgabewert nicht je nach Instanz variieren, obwohl er von Anruf zu Anruf variieren kann.
Ich empfehle jedoch, stattdessen eine Anmerkung zu verwenden, wenn Sie eine statische Methode in einer Schnittstelle verwenden möchten und dies nicht können. Sie erhalten die Funktionalität, die Sie suchen.
quelle
Ich denke, die kurze Antwort lautet "weil es keinen Nutzen hat". Um eine Schnittstellenmethode aufzurufen, benötigen Sie eine Instanz des Typs. Über Instanzmethoden können Sie beliebige statische Methoden aufrufen.
quelle
Ich denke, die Frage ist, dass C # für genau diese Art von Situation ein anderes Schlüsselwort benötigt. Sie möchten eine Methode, deren Rückgabewert nur vom Typ abhängt, für den sie aufgerufen wird. Sie können es nicht "statisch" nennen, wenn der Typ unbekannt ist. Sobald der Typ bekannt ist, wird er statisch. "Ungelöste statische Aufladung" ist die Idee - sie ist noch nicht statisch, aber sobald wir den empfangenden Typ kennen, wird es so sein. Dies ist ein perfektes Konzept, weshalb Programmierer immer wieder danach fragen. Aber es passte nicht ganz in die Art und Weise, wie die Designer über die Sprache dachten.
Da es nicht verfügbar ist, habe ich mich daran gemacht, nicht statische Methoden wie unten gezeigt zu verwenden. Nicht gerade ideal, aber ich sehe keinen sinnvolleren Ansatz, zumindest nicht für mich.
quelle
Wenn Sie also auf Schnittstellenvertragsmethoden zugreifen möchten, müssen Sie ein Objekt erstellen. Bei statischen Methoden ist dies immer nicht zulässig. Statische Klassen, Methoden und Variablen erfordern niemals Objekte und werden in den Speicher geladen, ohne ein Objekt dieses Bereichs (oder dieser Klasse) zu erstellen, oder Sie können sagen, dass keine Objekterstellung erforderlich ist.
quelle
Konzeptionell gibt es keinen Grund, warum eine Schnittstelle keinen Vertrag definieren könnte, der statische Methoden enthält.
Bei der aktuellen Implementierung der C # -Sprache ist die Einschränkung auf die Berücksichtigung der Vererbung einer Basisklasse und von Schnittstellen zurückzuführen. Wenn "Klasse SomeBaseClass" "Schnittstelle ISomeInterface" und "Klasse SomeDerivedClass: SomeBaseClass, ISomeInterface" ebenfalls die Schnittstelle implementiert, schlägt eine statische Methode zum Implementieren einer Schnittstellenmethode fehl, da eine statische Methode nicht dieselbe Signatur wie eine Instanzmethode haben kann (was der Fall wäre) in der Basisklasse vorhanden sein, um die Schnittstelle zu implementieren).
Eine statische Klasse ist funktional identisch mit einem Singleton und dient demselben Zweck wie ein Singleton mit sauberer Syntax. Da ein Singleton eine Schnittstelle implementieren kann, sind statische Schnittstellenimplementierungen konzeptionell gültig.
Es läuft also einfach auf die Begrenzung des C # -Namenkonflikts und der gleichnamigen statischen Methoden bei der Vererbung hinaus. Es gibt keinen Grund, warum C # nicht "aktualisiert" werden konnte, um statische Methodenverträge (Schnittstellen) zu unterstützen.
quelle
Wenn eine Klasse eine Schnittstelle implementiert, erstellt sie eine Instanz für die Schnittstellenmitglieder. Während ein statischer Typ keine Instanz hat, macht es keinen Sinn, statische Signaturen in einer Schnittstelle zu haben.
quelle