5 Stimmen

Vorschläge benötigt: Alternative zum Überladen von "is" und "as" Operatoren in .NET

Ich hoffe, dass diese Frage nicht als zu subjektiv angesehen wird - ich erwarte nicht wirklich eine endgültige Antwort, aber ich hoffe, dass die Meinung der anderen mir zumindest dabei hilft, mir meine eigene zu bilden.

Ich implementiere ein benutzerdefiniertes Typsystem, das eine Obermenge des klassischen OOP-Typsystems ist. In diesem Typsystem können Objektinstanzen zur Laufzeit kombiniert werden, um neue Instanzen zu bilden, wobei die individuellen Identitäten erhalten bleiben.

Dieser Code:

var p = new Person();
var pa = new Partner(p);

...führt zu einem einzigen kombinierten Objekt, wobei "p" und "pa" verschiedene OOP-konforme Sichten darauf sind. D.h., die Änderung eines Eigenschaftswertes in einer der Ansichten wirkt sich sofort auf jede andere Ansicht aus, die diese Eigenschaft ebenfalls enthält.

Das alles funktioniert gut und schön, aber es fehlen zwei wichtige APIs für die Abfrage von Typ-Identitäten. Ich würde wirklich gerne in der Lage sein, solchen Code zu schreiben:

if (p is Partner)
{
    (p as Partner).SomePartnerProperty = "...";
}

Das funktioniert natürlich nicht, weil das Verhalten von "is"- und "as"-Operatoren nicht über das hinaus überladen/erweitert werden kann, was die OOP-Regeln von .NET vorschreiben. Nichtsdestotrotz brauche ich diese Funktion in meinem Typsystem.

Mein erster Gedanke war, generische Erweiterungsmethoden zu verwenden, die sich an alle Instanzen meines Typsystems anhängen würden:

public static bool Is<T>(this BaseType target) where T : BaseType { ... }
public static T As<T>(this BaseType target) where T : BaseType { ... }

Abgesehen von der Frage des Namenskonflikts in Sprachen, die Groß- und Kleinschreibung nicht berücksichtigen, scheint dies in Bezug auf die Funktionalität in Ordnung zu sein:

if (p.Is<Partner>())
{
    p.As<Partner>().SomePartnerProperty = "...";
}

Ich muss mich jedoch fragen, ob dies wirklich die schönste und bequemste API ist, die man sich ausdenken kann.

Was würden Sie mir raten, um diese beiden Operatoren so zu implementieren, dass sie sich im Anwendungscode natürlich anfühlen?

UPDATE : Für alle, die sich über den Zweck eines solchen Systems wundern... Im Grunde genommen fällt jeder Typ in eine von zwei Kategorien: Identität ou Rolle . In dem oben genannten Beispiel ist Person eine Identität (durch Design), während Partner eine Rolle ist (wiederum durch Design - es hätte auch anders gestaltet werden können). Die Grundregel dieses Typsystems ist, dass eine beliebige Anzahl von Rollen mit einer beliebigen Identität zusammengesetzt werden kann, während die Identität selbst nur mit einer höheren Identität zusammengesetzt werden kann (z. B. kann eine Person zu einem Kontakt, aber niemals zu einem Unternehmen werden). Ein solches Typensystem ermöglicht es Anwendungen, z. B. mit Partnerobjekten transparent umzugehen, unabhängig davon, welche Identität sie haben (z. B. Person, Firma, Bank usw.).

1voto

Bobby Punkte 11139

Ich würde vorschlagen, zwei einfache Funktionen zu verwenden.

Die erste wäre (zumindest für mich) intuitiv mit einem Namen wie:

p.IsType(typeHere)
p.IsPartner()

und die zweite ein einfacher To-Call:

p.ToPartner()

Ich würde diese nicht als Generika implementieren (fühlt sich für mich einfach nicht richtig an), insbesondere das letzte nicht.

1voto

Steve Gilham Punkte 10979

Meine Lesung von dem, was die Frage ist nach wäre ein Mixin-System für C# - wie bei diskutiert Ist es möglich, Mixins in C# zu implementieren? . Unterm Strich gibt es einige Möglichkeiten, das meiste von dem zu bekommen, was eine korrekte Mixin-Syntax erlauben würde - obwohl ich persönlich nie gefunden habe, dass diese Workrounds ein gutes Kosten-Nutzen-Verhältnis ergeben.

0voto

Adam Robinson Punkte 176996

Um die vollständige Kontrolle über den Konvertierungsprozess zu behalten (und um sicherzustellen, dass Ihr Code tatsächlich ausgeführt wird), scheint Ihr Ansatz am sinnvollsten zu sein. Sie können explicit y implicit Operatoren, um die Typkonvertierung zu steuern (entweder durch implizite Konvertierungen oder explizit über Casting), aber es gibt genug "Probleme" in diesen Systemen, um sie zu einer weniger als wünschenswerten Wahl für das zu machen, was es aussieht, wie Sie versuchen, Architektur.

0voto

Brian Punkte 115257

Für C# denke ich, dass Ihr "erster Gedanke" (z.B.. x.Is<T>() ) sieht gut aus (aus dem Stegreif denke ich, dass ich das auch so machen würde).

Wenn Sie den Weg über F# gehen, könnten Sie aktive Muster verwenden, z. B.

match p with
| IsPartner pa -> DoPartnerStuff(pa)
| _ -> JustPersonStuff(p)

0voto

devio Punkte 36064

Nur ein Online-Brainstorming...

class Role {
    public Role(Person p) { p.liRoles.Add(this); }
}
class Partner : Role {
    public Partner(Person p) : base(p) {}
}
class Person { 
    List<IRole> liRoles;
    public T As<T>() { 
        foreach(IRole r in liRoles){ if r is T return r; }
        return null;
    }
    public bool Is<T>() { return As<T>() != null; }
}
var p = new Person();
var pa = new Partner(p);

sollte dies etwas wie folgt ermöglichen

if (p is Partner)
   (p as Partner).PartnerMethod();

Ich habe das allerdings nicht durch einen Compiler laufen lassen, es könnte also völlig falsch sein ;)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X