2009-03-13 4 views
5

Ich habe ein kleines Problem mit einer etwas einfachen Wrapperklasse, die ich habe.C# mimic überschreibt den Zuweisungsoperator (=)

Es sieht ungefähr so ​​aus:

public class Wrapper<T> 
{ 
    private T _value; 

    public Wrapper<T>(T value) 
    { 
    _value = value; 
    } 

    public static implicit operator Wrapper<T>(T value) 
    { 
    return new Wrapper<T>(value); 
    } 

    public static implicit operator T(Wrapper<T> value) 
    { 
    return value._value; 
    } 
} 

ich die impliziten Wandler aus und T außer Kraft gesetzt habe, so verhält es sich fast wie eine Instanz von T selbst.

z.B.

Wrapper<int> foo = 42; 

Allerdings habe ich ein kleines Problem bekommen, wenn eine Instanz des Wrapper zu einem anderen zuweisen, da ich nur den Wert der zweiten Wrapper-Klasse zuordnen will.

So jetzt habe ich dies zu tun:

Wrapper<int> foo = 42; 
Wrapper<int> bar = (int)foo; 

Oder aussetzen _value eine Eigenschaft öffentlich durch.

Da dies jedoch in einer Bibliothek ist und ich möchte nicht, dass der Benutzer darauf angewiesen ist, sich daran zu erinnern, haben Sie eine Idee, wie ich den Zuweisungsoperator übersteuern könnte?

Das Problem beim bloßen Ändern des Zeigers (wie beim Zuordnen einer Klasseninstanz zu einem anderen) ist, dass ich ein Wörterbuch mit Zeigern auf diese Wrapper-Objekte habe, so dass ich sie nicht ständig ändern kann Das Wörterbuch würde dann nicht mehr zusammenpassen.

Ich kann sehen, ob dies etwas verwirrend ist, also wenn ich heraus wichtig etwas übrig haben, bitte :-)

+0

wurde das jemals gelöst? Ich habe ein ähnliches Problem - außer ich erstelle etwas, um grundlegende Arten z. float und möchte MyType machen können < float > a = 1f; a = 2f; ohne eine neue Instanz zu erstellen ... werden Referenzen wirklich durch Magie aktualisiert? Ich würde nur versuchen, aber ich bin nicht davon überzeugt, dass es ein gutes Experiment ist, denn wenn es funktioniert, könnte es blindes Glück sein für alles, was ich weiß ... – jheriko

Antwort

1

fragen sich frei fühlen Wenn Sie bei Nullable aussehen <T> ..., die eine sehr tut Ähnlich wie bei dem, was Sie hier tun, macht es den internen Wert mit einer .Value -Eigenschaft verfügbar.

Das Problem in nur den Zeiger zu ändern (wie es, wenn eine Klasseninstanz in eine andere Zuordnung), ist, dass ich ein Wörterbuch der Zeiger auf diese Wrapper-Objekte haben, so kann ich sie nicht haben, das ständig ändert , da das Wörterbuch dann nicht mehr übereinstimmt.

Ich bin mir nicht sicher, ob ich dem folge, was genau speicherst du im Wörterbuch? Wenn Sie Referenzen speichern, aktualisiert die CLR diese bei Bedarf.

+1

Wie ich es verstanden, Nullable wurde einige zusätzliche Compiler-Unterstützung gegeben, um diese Art von Zuordnung zu decken Szenarien. – ajlane

3

Sie könnten Wrapper <T> ein struct machen. Ich bin mir jedoch nicht sicher, ob dies zu Ihrem Anwendungsdesign passt oder nicht.

5

Da der Zuweisungsoperator nicht überladen werden kann, gibt es keine wirklich gute Lösung. Wie eine andere Person bereits erwähnt hat, gibt Ihnen die Verwendung einer Struktur die Semantik der Zuweisung, die Sie wollen, aber dann werden Sie mit einer Wertesemantik konfrontiert - oft keine gute Sache.

Eine Möglichkeit ist, den Konstruktor zu überlasten:

public Wrapper(Wrapper<T> w) 
{ 
    _value = w._value; 
} 

, die in dieser Syntax führen würde:

Wrapper<int> foo = 42; 
Wrapper<int> bar = new Wrapper<int>(foo); 

Obwohl ausführlicher als das, was Sie haben, ist es besser liest.

Oder Sie könnten eine Clone Methode (nicht die ICloneable Schnittstelle) hinzufügen, so dass Sie schreiben können:

Wrapper<int> bar = foo.Clone(); 

Sie könnten wirklich kreativ und einige Betreiber überlasten, es im Wesentlichen nichts auszukommen. Ich würde das aber nicht empfehlen. Die Verwendung von Operatorüberladung für diese Art von Dingen macht den Code normalerweise kryptisch und bricht oft.

0

Dies sind die Eigenschaften für. Sie ermöglichen Ihnen zu definieren, welche Zuordnung bedeutet. Sie können es nicht für eine Klasse oder Struktur selbst definieren, da sie bereits von der Sprache definiert sind, um notwendige Dinge zu tun. Fügen Sie der Klasse einfach eine Value-Eigenschaft hinzu.

Alternativ können Sie Ihre Frage bearbeiten, um eine ausführlichere Beschreibung Ihres Designs zu geben und wie dieser Wrapper darin passt, da jemand einen einfacheren Ansatz vorschlagen kann.

0

Ich habe es mir nur angeschaut, was die Klasse zu einer Struktur macht, ist wirklich keine Option, da sie eine gewisse Logik im parameterlosen Konstruktor hat und eine abstrakte Klasse erbt, die interne abstrakte Funktionen enthält.

Ich kann eine Schnittstelle nicht verwenden, da diese Funktionen öffentlich gemacht würden, was die Logik völlig durchbrechen würde.

Ich kann die gesamte Klasse veröffentlichen, wenn das hilfreich wäre, aber es ist etwas lang (130 Zeilen) Oder ich könnte auf einem separaten Server werfen, wenn das besser wäre? (Obwohl es die Integrität dieser Frage weh tut, wie ich es kann schließlich von diesem Server löschen)

erklärt sich auch die Klasse wirklich schwierig ist, ohne eine vollständige Essayistik: -/

Auf jeden Fall werde ich versuchen, illustriere das Problem, das ich habe.

Angenommen 2 Tabellenklassen: Customer und Usertable:

public class CustomerTable 
{ 
    Wrapper<string> Name; 
} 

public class UserTable 
{ 
    Wrapper<string> Name; 
} 

Nun das Problem ist, dass einige andere Entwickler, können Sie den obigen Code wie folgt verwenden:

CustomerTable customer = new CustomerTable(); 
UserTable user = new UserTable(); 
user.Name = customer.Name; // This breaks my internal dictionary 

Was die Entwickler getan hatte, sollte , damit es funktioniert, war:

user.Name = (string)customer.Name; 

Das Problem ist jedoch, wer in ihrer richtigen Meinung würde darüber nachdenken, wenn Code schreiben?

Auch wenn ich eine Value-Eigenschaft verwendet wird, würde der Entwickler noch

user.Name = customer.Name.Value; // or user.Name.Value = .... 

Und wieder kann der Entwickler vergessen, dies zu schreiben, haben sich daran zu erinnern, und ganz plötzlich er Ausnahmen bekommt, oder noch schlimmer: Daten, die ist nicht in der Datenbank gespeichert.

Also mein Problem ist wirklich, dass ich möchte, dass der Wrapper vollständig transparent ist (es sollte benutzbar sein, als wäre es tatsächlich die Klasse/das primitive Wrapping). Wenn ich jedoch von einem Wrapper zu einem anderen lege, bricht meine interne Logik.

Puh viel schreiben, und eine Menge Code - lassen Sie mich wissen, wenn ich das Schreiben übertreiben.

+2

Ich würde die Value-Eigenschaft verwenden. Es ist nie eine gute Idee, implizit einen Typ in beide Richtungen zu werfen. Downstream-Entwickler werden sehr schnell lernen, wie Sie Ihre Bibliothek korrekt verwenden, wenn ihre naiven Zuordnungen nicht kompiliert werden können. – ajlane

0

Implizit können Sie Ihren Wrapper nicht auf beide Arten darstellen.

public class DBValue<T> 
{ 
    public static implicit operator DBValue <T>(T value) 
    { 
     return new DBValue<T>(value); 
    } 

    public static explicit operator T(DBValue <T> dbValue) 
    { 
     return dbValue.Value; 
    } 

    private readonly T _value; 
    public T Value { get { this._value; } } 

    public DBValue(T value) 
    { 
     this._value = value; 
    } 
} 

von DBValue<T> zu T Casting ist eine verlustbehaftete Umwandlung (als Minimum, verlieren Sie die Tatsache, dass es ein Wert aus der Datenbank ist) und durch Best-Practice sollte explizit genannt werden. Wenn Sie durch das Umwandeln von DBValue<T> in T nichts verlieren, können Sie auch einfach Eigenschaften verwenden, die T zurückgeben.

Im Grunde haben Sie bereits gesehen, warum Sie nicht versuchen sollten: Wenn ein DBValue durch T ersetzt werden kann und umgekehrt, woher weiß der Compiler (oder Entwickler), welchen er wählen soll?

Erfordern Down-Stream-Entwickler zu schreiben:

string value = MyProperty.Value 

oder

string value = (string)MyProperty 

statt

string value = MyProperty 

... nicht alle, dass belastende ist und stellt sicher, dass Jeder weiß genau, was vor sich geht.

EDIT:

tatsächlich Um die Frage zu beantworten, können Sie keine Referenzzuordnung außer Kraft setzen - oder es müssen so aussehen wie Sie machen - aber Sie sollten nicht wirklich brauchen.

+0

Ich sollte beachten, dass ich diesen Fehler tatsächlich ein paar Mal selbst gemacht habe - es hat mir vor allem Probleme mit Zahlen verursacht: d. H. Was ist der Typ von 1 + meinWert, wenn meinWert eine Klasse ist, die doppelt ausgibt? – ajlane

0

A J Lane Ich sehe, was Sie meinen, und ich denke, Sie haben Recht - ich wollte es nur so einfach wie möglich machen, um die Bibliothek zu verwenden.

Der Grund für die implizite Umwandlung von DbValue zu T ist einfach Funktionen, die

T. erwartet

zum Beispiel

literalSomething.Text = Server.HtmlEncode(SomeTable.SomeStringColumn); 

statt

literalSomething.Text = Server.HtmlEncode((string)SomeTable.SomeStringColumn); 

Dies erfordert die Besetzung zu sei implizit.

Das sagte ich gerade lesen Sie Ihren Kommentar während der Eingabe dieser, und ich kann sehen, das ist ziemlich das Problem.

Ich denke, ich gehe zurück, um Wert durch eine Eigenschaft offen zu legen, es erfordert nur den Entwickler, mehr zu tippen, und irgendwie macht der Code hässlich, denke ich.

Gerade DbValue vorstellen:

if (someValue.Value.HasValue) // someValue is DbValue<int?> 

Aber dann wieder ist es wahrscheinlich besser mit „hässlich“ Code als Code, der aus unterschiedlich verhält, was man durch bloßes Lesen sie erwarten würde.

Ich denke, diese Frage endet wirklich als "Best Practice" -Frage.

Zum Schluss werde ich eine Value-Eigenschaft erstellen und diese anstelle von impliziten Umwandlungen verwenden, und der Entwickler, der die Bibliothek verwendet, muss damit leben.

Vielen Dank für Ihre Eingaben :-)

+0

Für besser lesbaren (weniger "hässlichen") Code könnten Sie immer int zuweisen? Wert = someValue.Value; und dann wie erwartet fortfahren. – ajlane

0

Diese alte Post Stills zusätzliche Informationen benötigt Vollständigkeit. Es ist offensichtlich, dass das ursprüngliche gewünschte Verhalten nicht erreicht werden kann, da der Operator = nicht überladen werden kann, und ebenso kann C# nicht dazu verleitet werden, ein Objekt auf seinen eigenen Typ zu werfen ... es wird immer auf eine Klassenreferenz-Zuweisung herunterkochen. Aber Steffens weitere Beiträge zeigen, dass die Wrapper-Klasse nicht nur mit lokalen Variablen verwendet wird, sondern als Klassenfeldtyp. Die gewünschte Semantik kann verwendet werden UND die Integrität des internen Wörterbuchs wird beibehalten, indem Klasseneigenschaften anstelle von öffentlichen Feldern verwendet werden.


Auch die ursprüngliche gegeben Wrapper<T> Klasse halten beide mit ihren impliziten Betreiber, hier ist Code, der funktionieren würde:

public class CustomerTable 
{ 
    private Wrapper<string> _Name; 
    public Wrapper<string> Name { 
     get { return _Name; } 
     set { _Name = (string)value; } 
    } 
} 

public class UserTable 
{ 
    private Wrapper<string> _Name; 
    public Wrapper<string> Name { 
     get { return _Name; } 
     set { _Name = (string)value; } 
    } 
} 

Wenn diese Änderung vorgenommen wurden, wäre es nicht vorhandenen Code brechen, da es immer noch erlaubt verschiedene Modi der Einstellung der Eigenschaft:

Da dies immer noch nicht die ursprüngliche Frage beantwortet, verwenden Sie den Wrapper Die Klasse könnte immer noch problematisch sein, wenn sie außerhalb des Klasseneigenschaftskontexts verwendet wird, d. h. zwischen Objekt usw. übergeben wird. Vielleicht könnte die gesamte Wrapper-Klasse mit dem richtigen Klassendesign einschließlich der Verwendung von Eigenschaft set/get accessors eliminiert werden.