Frage

Kann ich Schnittstellen angeben, wenn ich ein Mitglied deklariere?

Nachdem ich eine Weile über diese Frage nachgedacht hatte, kam mir der Gedanke, dass eine Sprache vom Typ „Static Duck“ tatsächlich funktionieren könnte.Warum können vordefinierte Klassen zur Kompilierungszeit nicht an eine Schnittstelle gebunden werden?Beispiel:

public interface IMyInterface
{
  public void MyMethod();
}

public class MyClass  //Does not explicitly implement IMyInterface
{
  public void MyMethod()  //But contains a compatible method definition
  {
    Console.WriteLine("Hello, world!");
  }
}

...

public void CallMyMethod(IMyInterface m)
{
  m.MyMethod();
}

...

MyClass obj = new MyClass();
CallMyMethod(obj);     // Automatically recognize that MyClass "fits" 
                       // MyInterface, and force a type-cast.

Kennen Sie Sprachen, die eine solche Funktion unterstützen?Wäre es in Java oder C# hilfreich?Ist es in irgendeiner Weise grundlegend fehlerhaft?Ich verstehe, dass Sie eine Unterklasse von MyClass erstellen und die Schnittstelle implementieren oder das Adapter-Entwurfsmuster verwenden könnten, um dasselbe zu erreichen, aber diese Ansätze scheinen einfach unnötiger Standardcode zu sein.

War es hilfreich?

Lösung

Statisch typisierte Sprachen per Definition überprüfen Arten bei kompilieren Zeit , nicht Laufzeit . Einer der offensichtlichen Probleme mit dem oben beschriebenen System ist, dass die Compiler-Typen wird überprüfen, wenn das Programm kompiliert wird, nicht zur Laufzeit.

Nun könnte man mehr Intelligenz in die Compiler bauen, so könnte es derive Typen, anstatt die Programmierer explizit mit deklarieren Typen; der Compiler in der Lage könnte, dass MyClass implementiert eine MyMethod() Methode und behandelt diesen Fall entsprechend, ohne die Notwendigkeit zu erklären ausdrücklich, Schnittstellen zu sehen (wie Sie vorschlagen). Ein solcher Compiler könnte Typinferenz, wie Hindley-Milner nutzen.

Natürlich einige statisch typisierten Sprachen wie Haskell schon etwas tun ähnlich zu dem, was Sie vorschlagen; der Haskell Compiler in der Lage, Typen zu schließen (die meiste Zeit) ohne die Notwendigkeit, sie ausdrücklich zu erklären. Aber natürlich, Java / C # nicht über diese Fähigkeit hat.

Andere Tipps

Eine ganz neue Antwort auf diese Frage, Go hat genau diese Funktion . Ich denke, es ist wirklich cool und klug (obwohl ich daran interessiert sein zu sehen, wie es spielt im wirklichen Leben aus) und ein dickes Lob an dem Denken davon.

Wie dokumentiert in der offiziellen Dokumentation (als Teil der Tour von Go, mit Beispiel-Code) :

  

Schnittstellen implementiert implizit

     

Eine Art implementiert eine Schnittstelle, die durch ihre Methoden zu implementieren. Es gibt   keine explizite Absichtserklärung, keine „Geräte“ Schlüsselwort.

     

Implizite Schnittstellen entkoppeln die Definition einer Schnittstelle von seinem   Implementierung, die dann in jedem Paket erscheinen könnte, ohne   prearrangement.

Wie wäre es Vorlagen in C ++ verwenden?

class IMyInterface  // Inheritance from this is optional
{
public:
  virtual void MyMethod() = 0;
}

class MyClass  // Does not explicitly implement IMyInterface
{
public:
  void MyMethod()  // But contains a compatible method definition
  {
    std::cout << "Hello, world!" "\n";
  }
}

template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
  m.MyMethod();  // instantiation succeeds iff MyInterface has MyMethod
}

MyClass obj;
CallMyMethod(obj);     // Automatically generate code with MyClass as 
                       // MyInterface

habe ich nicht wirklich diesen Code kompiliert, aber ich glaube, es ist praktikabel und ein ziemlich trivial C ++ -. Isierung des ursprünglichen vorgeschlagen (aber nicht funktionierenden) Code

Ich sehe den Punkt nicht. Warum nicht explizit sein, dass die Klasse die Schnittstelle implementiert und hat mit ihm getan? die Schnittstelle implementiert ist, was andere Programmierer sagt, dass diese Klasse in der Art und Weise soll die Schnittstelle definiert verhalten. mit einfach den gleichen Namen und Unterschrift auf einem Verfahren keine Garantien vermitteln, dass die Absicht des Designers ähnliche Aktionen mit dem Verfahren durchzuführen war. Das mag sein, aber warum es für Interpretation lassen (und Missbrauch)?

Der Grund, warum Sie mit diesem Erfolg in dynamischen Sprachen „weg“ können, hat mehr mit TDD zu tun, als mit der Sprache selbst. Meiner Meinung nach, wenn die Sprache bietet die Möglichkeit, diese Art von Führung, anderen zu geben, die / verwenden Sie den Code zu sehen, sollten Sie es verwenden. Es verbessert die eigentlich Klarheit und die wenigen zusätzlichen Zeichen wert. In dem Fall, dass Sie den Zugriff nicht, dies zu tun haben, dann dient einem Adapter den gleichen Zweck ausdrücklich erklärt, wie die Schnittstelle zu der anderen Klasse bezieht.

F # unterstützt statische Ente eingeben, wenn auch mit einem Haken: Sie Mitglied Einschränkungen verwenden. Details sind in diesem Blog-Eintrag .

Beispiel aus dem zitierten Blog:

let inline speak (a: ^a) =
    let x = (^a : (member speak: unit -> string) (a))
    printfn "It said: %s" x
    let y = (^a : (member talk: unit -> string) (a))
    printfn "Then it said %s" y

type duck() =
    member x.speak() = "quack"
    member x.talk() = "quackity quack"
type dog() =
    member x.speak() = "woof"
    member x.talk() = "arrrr"

let x = new duck()
let y = new dog()
speak x
speak y

Die meisten Sprachen der ML-Familie werden unterstützt Strukturtypen mit Inferenz und eingeschränkten Typschemata, das ist die geekige Sprachdesigner-Terminologie, die höchstwahrscheinlich das ist, was Sie in der ursprünglichen Frage mit dem Ausdruck „statisches Duck-Typing“ meinen.

Zu den beliebtesten Sprachen dieser Familie, die mir in den Sinn kommen, gehören:Haskell, Objective Caml, F# und Scala.Dasjenige, das Ihrem Beispiel am ehesten entspricht, wäre natürlich Objective Caml.Hier ist eine Übersetzung Ihres Beispiels:

open Printf

class type iMyInterface = object
  method myMethod: unit
end

class myClass = object
  method myMethod = printf "Hello, world!"
end

let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod

let myClass = new myClass

callMyMethod myClass

Notiz:Einige der von Ihnen verwendeten Namen müssen geändert werden, um der OCaml-Vorstellung der Groß- und Kleinschreibung von Bezeichnern zu entsprechen, aber ansonsten ist dies eine ziemlich einfache Übersetzung.

Erwähnenswert ist auch, dass weder die Typanmerkung in der callMyMethod Funktion noch die Definition der iMyInterface Der Klassentyp ist unbedingt erforderlich.Objective Caml kann alles in Ihrem Beispiel ohne jegliche Typdeklarationen ableiten.

Typoskript!

Nun, ok ... So ist es ein Javascript Obermenge und vielleicht auch nicht eine „Sprache“ darstellt, aber diese Art von statischer Ente-Typisierung ist von entscheidenden Bedeutung in Typoskript.

eingeben Bild Beschreibung hier

Boo definitiv ist eine statische Ente typisierte Sprache: http://boo.codehaus.org/Duck + Typing

Ein Auszug:

  

Boo ist eine statisch typisierte Sprache,   wie Java oder C #. Dies bedeutet, dass Ihr boo   Anwendungen werden so schnell laufen etwa als   diejenigen, die in anderen codierten statisch typisierte   Sprachen für .NET oder Mono. aber der Einsatz von   eine statisch typisierte Sprache manchmal   zwingt Sie zu einem unflexiblen und   ausführlicher Stil Codierung, mit der   manchmal notwendig Typdeklarationen   (Wie „x als int“, aber das ist nicht   notwendig oft aufgrund boo des Typs   Inference) und manchmal notwendig   Typ Abgüsse (siehe Casting-Typen). Boo   Unterstützung für Typ Inference und   schließlich Generika helfen hier, aber ...

     

Manchmal ist es angebracht, aufgeben   das Sicherheitsnetz durch statische vorgesehen   Tippen. Vielleicht möchten Sie einfach erkunden   eine API, ohne zu viel Sorgen über   Methodensignaturen oder vielleicht sind Sie   Erstellen von Code, der auf externe Gespräche   Komponenten wie COM-Objekte. Entweder   Art und Weise sollte die Wahl nicht sein   Mine.

     

Neben den normalen Typen wie   Objekt, int, string ... boo hat ein   spezielle Art „Ente“ genannt. Der Begriff   wird durch die rubin Programmierung inspiriert   Duck Typing Funktion der Sprache ( "Wenn es   läuft wie eine Ente und quakt wie eine   Ente, muss es eine Ente "sein).

Neue Versionen von C ++ in Richtung der statischen Ente eingeben bewegen. Sie können eines Tages (? Heute) schreiben Sie etwas wie folgt aus:

auto plus(auto x, auto y){
    return x+y;
}

, und es würde nicht kompilieren, wenn kein passender Funktionsaufruf für x+y gibt.

Wie für Ihre Kritik:

  

Eine neue „CallMyMethod“ wird für jede unterschiedliche Art erstellt Sie es passieren, so ist es nicht wirklich Inferenz geben.

Aber es ist Typinferenz (man kann sagen, wo foo(bar) foo eine Templat-Funktion ist), und hat die gleiche Wirkung, es sei denn, es ist zeiteffizienter und mehr Platz im kompilierten Code nimmt.

Ansonsten würden Sie die Methode während der Laufzeit nachschlagen müssen. Sie müßten einen Namen finden, dann überprüfen Sie, dass der Name ein Verfahren mit den richtigen Parametern hat.

Oder Sie würden alle diese Informationen über passende Schnittstellen speichern haben, und schauen Sie in jeder Klasse, die eine Schnittstelle übereinstimmt, dann fügen Sie automatisch diese Schnittstelle.

In jedem Fall, die Sie implizit zu können und aus Versehen die Klassenhierarchie brechen, die für ein neues Feature schlecht ist, weil es gegen die Gewohnheiten geht, was Programmierer von C # / Java verwendet werden. Mit C ++ Vorlagen, wissen Sie bereits, Sie in einem Minenfeld sind (und sie sind auch das Hinzufügen von Funktionen ( „Konzepte“), damit Einschränkungen für Template-Parameter).

Strukturtypen in Scala tun so etwas.

Siehe Statisch geprüft‚Duck Typing‘in Scala

Ein Pre-Release-Design für Visual Basic hatte 9-Unterstützung für statische Typisierung Ente mit dynamischen Schnittstellen, aber sie schneiden Sie die Funktion * , um pünktlich zu versenden.

D ( http://dlang.org ) ist eine statisch kompilierte Sprache und bietet Ente-Eingabe über Wrap () und unwrap () ( http://dlang.org/phobos-prerelease/std_typecons.html # .unwrap ).

Kristall ist eine statisch Ente typisierte Sprache. Beispiel :

def add(x, y)
  x + y
end

add(true, false)

Der Aufruf von add verursacht diesen Übersetzungsfehler:

Error in foo.cr:6: instantiating 'add(Bool, Bool)'

add(true, false)
^~~

in foo.cr:2: undefined method '+' for Bool

  x + y
    ^

In der neuesten Version meiner Programmiersprache Heron es etwas ähnliches durch einen strukturell-Subtypisierung Zwang unterstützt Betreiber genannt as. Anstatt also:

MyClass obj = new MyClass();
CallMyMethod(obj);

Sie würde schreiben:

MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);

Genau wie in Ihrem Beispiel, in diesem Fall MyClass muß nicht explizit IMyInterface implementieren, aber wenn es die Besetzung implizit geschehen tut konnte und der as Betreiber verzichtet werden kann.

Ich schrieb ein bisschen mehr über die Technik, die ich in die explizite Strukturunter Typisierung nennen dieser Artikel .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top