Ich habe eine Funktion, die dieselbe Art von Objekten zurückgibt (Abfrageergebnisse), aber keine gemeinsamen Eigenschaften oder Methoden hat. Um einen gemeinsamen Typ zu haben, habe ich eine leere Schnittstelle als Rückgabetyp verwendet und diese auf beiden "implementiert".
Das klingt natürlich nicht richtig. Ich kann mich nur trösten, indem ich mich an die Hoffnung klammere, dass diese Klassen eines Tages etwas gemeinsam haben und ich diese gemeinsame Logik auf meine leere Oberfläche verschieben werde. Dennoch bin ich nicht zufrieden und denke darüber nach, ob ich zwei verschiedene Methoden haben und als nächstes bedingt anrufen soll. Wäre das ein besserer Ansatz?
Mir wurde auch gesagt, dass .NET Framework leere Schnittstellen für Tagging-Zwecke verwendet.
Meine Frage ist: Ist eine leere Schnittstelle ein starkes Zeichen für ein Designproblem oder wird sie häufig verwendet?
EDIT : Für Interessierte fand ich später heraus, dass diskriminierte Gewerkschaften in funktionalen Sprachen die perfekte Lösung für das sind, was ich erreichen wollte. C # scheint diesem Konzept noch nicht freundlich zu sein.
EDIT : Ich habe ein längeres Stück über dieses Problem geschrieben und das Problem und die Lösung im Detail erklärt.
quelle
Antworten:
Obwohl es den Anschein hat, dass es für diesen Anwendungsfall ein Entwurfsmuster gibt (viele haben jetzt "Marker-Schnittstelle" erwähnt), glaube ich, dass die Verwendung einer solchen Praxis ein Hinweis auf einen Code-Geruch ist (zumindest die meiste Zeit).
Wie @ V4Vendetta veröffentlicht hat, gibt es eine statische Analyseregel, die darauf abzielt: http://msdn.microsoft.com/en-us/library/ms182128(v=VS.100).aspx
Dies ist die zitierte MSDN-Empfehlung:
Dies spiegelt auch den Abschnitt "Kritik" des bereits veröffentlichten Wikipedia-Links wider.
quelle
attributes
sind sie zwar die „richtige“ Vorgehensweise, aber schwieriger zu implementieren und zu verwenden, was in der Praxis ihrer Verwendung entgegenwirkt.Sie geben an, dass Ihre Funktion "in bestimmten Fällen völlig unterschiedliche Objekte zurückgibt" - aber wie unterschiedlich sind sie? Könnte einer ein Stream-Writer sein, ein anderer eine UI-Klasse, ein anderer ein Datenobjekt? Nein ... ich bezweifle es!
Ihre Objekte haben möglicherweise keine gemeinsamen Methoden oder Eigenschaften, sie ähneln sich jedoch wahrscheinlich in ihrer Rolle oder Verwendung. In diesem Fall erscheint eine Markierungsschnittstelle völlig angemessen.
quelle
Wenn nicht als Marker-Schnittstelle verwendet , würde ich sagen, dass dies ein Code-Geruch ist.
Eine Schnittstelle definiert einen Vertrag, an den sich der Implementierer hält. Wenn Sie leere Schnittstellen haben, über die Sie keine Reflexion verwenden (wie dies bei Markierungsschnittstellen der Fall ist), können Sie auch
Object
den (bereits vorhandenen) Basistyp verwenden.quelle
object
wäre zu allgemein und würde keinen Hinweis auf die Rückgabe "Art" geben. Die Schnittstelle hilft mir, meine Optionen für die Interpretation des Rückgabewerts der Funktion einzugrenzen. Ich verstehe jedoch Ihren Kommentar in Bezug auf die mangelnde Klärung der Ähnlichkeit der von mir zurückgegebenen Objekte.Sie haben Ihre eigene Frage beantwortet ... "Ich habe eine Funktion, die in bestimmten Fällen völlig unterschiedliche Objekte zurückgibt." ... Warum möchten Sie dieselbe Funktion haben, die völlig unterschiedliche Objekte zurückgibt? Ich sehe keinen Grund dafür, dass dies nützlich ist. Vielleicht haben Sie einen guten. In diesem Fall teilen Sie uns dies bitte mit.
EDIT: In Anbetracht Ihrer Klarstellung sollten Sie in der Tat eine Marker-Schnittstelle verwenden. "völlig anders" ist ganz anders als "sind die gleiche Art". Wenn sie völlig anders wären (nicht nur, dass sie keine gemeinsamen Mitglieder haben), wäre das ein Code-Geruch.
quelle
Wie viele wahrscheinlich bereits gesagt haben, hat eine leere Schnittstelle eine gültige Verwendung als "Markierungsschnittstelle".
Die wahrscheinlich beste Verwendung, die ich mir vorstellen kann, besteht darin, ein Objekt als zu einer bestimmten Teilmenge der Domäne gehörend zu bezeichnen, die von einem entsprechenden Repository verarbeitet wird. Angenommen, Sie haben verschiedene Datenbanken, aus denen Sie Daten abrufen, und Sie haben jeweils eine Repository-Implementierung. Ein bestimmtes Repository kann nur eine Teilmenge verarbeiten und sollte keine Instanz eines Objekts aus einer anderen Teilmenge erhalten. Ihr Domain-Modell könnte folgendermaßen aussehen:
//Every object in the domain has an identity-sourced Id field public interface IDomainObject { long Id{get;} } //No additional useful information other than this is an object from the user security DB public interface ISecurityDomainObject:IDomainObject {} //No additional useful information other than this is an object from the Northwind DB public interface INorthwindDomainObject:IDomainObject {} //No additional useful information other than this is an object from the Southwind DB public interface ISouthwindDomainObject:IDomainObject {}
Ihre Repositorys können dann generisch für ISecurityDomainObject, INorthwindDomainObject und ISouthwindDomainObject erstellt werden. Anschließend wird zur Kompilierungszeit überprüft, ob Ihr Code nicht versucht, ein Sicherheitsobjekt an die Northwind-Datenbank (oder eine andere Permutation) zu übergeben. In solchen Situationen bietet die Schnittstelle wertvolle Informationen zur Art der Klasse, auch wenn sie keinen Implementierungsvertrag enthält.
quelle