2012-06-21 3 views
11

Ich habe zwei verschiedene Arten von Strings, die ich verteile und in meinem Code verwende, und die beiden sind eng miteinander verwandt, sollten aber nicht füreinander verwechselt werden. Ich dachte, ich könnte mir helfen, Fehler zu vermeiden, indem ich zwei Klassen habe, die nur Strings sind, aber unterschiedliche Namen haben, so dass Methodensignaturen (und Typinkompatibilität im Allgemeinen) die semantische Bedeutung der zwei verschiedenen Stringtypen erzwingen würden. Zur gleichen Zeit wollte ich nicht von something = "foo"; zu something.Value = "foo"; in hundert Orten umgestalten.Wie kann ich abgeleitete "Shell" -Klassen deklarieren, die nur als Umbenennungen fungieren?

Erster Gedanke:

private class FirstKind : string { } 
private class SecondKind : string { } 

Die Idee ist, dass, wenn ich

void MyMethod(FirstKind varOne, SecondKind varTwo) {...} 

haben, und dann versuchen, es mit MyMethod(secondKindVar, firstKindVar); zu nennen, ich einen Compiler-Fehler bekommen würde.

Wand Ich schlug: string ist versiegelt.

Zweiter Gedanke: Erstellen Sie generische KindOf<T> Klasse, die nichts tun würde, aber nehmen und spuckt den Wert mit impliziten Konvertierungsoperatoren aus. Wie folgt aus:

private class KindOf<T> 
{ 
    public T Value { get; set; } 
    public KindOf() { Value = default(T); } 
    public KindOf(T val) { Value = val; } 
    public static implicit operator T(KindOf<T> kindOf) { return kindOf.Value; } 
    public static implicit operator KindOf<T>(T value) { return new KindOf<T>(value); } 
} 

So kann ich etwas tun könnte:

private class FirstKind : KindOf<string> { } 
private class SecondKind : KindOf<string> { } 

Wand schlug ich: nichts von der KindOf<T> Klasse scheint zu erben: weder Konstruktoren noch Operatoren in den abgeleiteten Typen existieren, was bedeutet, Ich muss im Wesentlichen die gesamte Basisklasse in den abgeleiteten Klassen neu implementieren. Code kopieren. Jawohl.

So fühle ich mich, als würde ich hier etwas Grundlegendes vermissen und vielleicht bin ich im Unkraut. Irgendwelche Ideen für das, was ich versuche zu tun?

+1

Vielleicht könnten Sie klären, was "zwei verschiedene Arten von Strings" bedeutet? –

+1

Von Kopf bis Kopf müssen Sie möglicherweise nur die impliziten Operatoren und Konstruktoren in den Unterklassen implementieren. Ja, so ähnlich wie beim Kopieren von Code, aber es verwendet zumindest keinen verschachtelten Prozess oder Vererbung, wenn die Vererbung wirklich nichts kommuniziert _real_ (rhetorisch, was ist ein "KindOf" überhaupt und bedeutet es wirklich irgendetwas?) Und ist einfach dort, um Code-Duplizierung über Klassen zu reduzieren, die nichts gemeinsam haben außer ähnlicher Syntax (aber verschiedene Verwendungen) –

+0

Vereinbart, die Klassen sollten wirklich nicht in der gleichen Vererbungshierarchie sein, besonders wenn man bedenkt, dass man sich nicht auf sie beziehen will durch einen gemeinsamen Typ wollen Sie, dass sie unterschiedlich bleiben. – Servy

Antwort

0

Ich würde das nur für Strings tun.

abstract class MyTypedString 
{ 
    protected MyTypedString(string value) 
    { 
    Value = value; 
    } 
    public string Value { get; private set; } 
} 

Dann könnten Sie diese abstrakte Klasse fertig machen mit Ihrem FirstKind und SecondKind. Dadurch bleibt das Objekt unveränderbar wie Strings. Ich würde dies nicht implizit Konvertierungsoperatoren geben.

+0

Das ist im Wesentlichen das gleiche wie sein zweites Beispiel (nur weniger schön). –

0

Betreiber wird immer noch funktionieren, müssen Sie nur das .Value Mitglied Ihrer kindof Klasse verweisen:

FirstKind stringFoo = new FirstKind("foo"); 
FirstKind stringQux = new FirstKind("qux"); 

// String concatenation operator 
FirstKind stringTar = new FirstKind(stringFoo.Value + stringQux.Value); 

aber wie Sie sagen, wird es keine „type-checking“, wenn Sie die beiden mischen, z.B.

SecondKind stringBar = new SecondKind("bar"); 
FirstKind stringHal = new FirstKind(stringFoo.Value + stringBar.Value); // this will succeed, even though you intend stringFoo and stringBar to be incompatible 

Als Betreiber Teil einer Klasse sind -Schnittstelle, dann müssen Sie neu implementieren sie - aber Sie können einfach die enthaltene Klasse wickeln Implementierung, so dass Sie nur langweilig Grunzen-Arbeit zu tun, müssen diese zur Arbeit kommen .

0

Sie könnten versuchen, den Konvertierungsoperator implicit in Ihrer Klasse auf eine Zeichenfolge zu überladen. Dann müssten Sie Ihre Referenzen auf .Value nicht aktualisieren. Die Klassen müssen separat bleiben und Sie müssen Konstruktoren für sie erstellen, die den Zeichenfolgenwert akzeptieren. Die beste Methode wäre, die Zeichenfolge readonly beizubehalten, sodass die Klasse unveränderlich wird. Imitiert die String-Klasse, die es darstellen soll.

public class FirstKind 
{ 
     private readonly string value; 
     public FirstKind(string v) { this.value = v }; 
     public static implicit operator string(FirstKind v){ return v.value; } 
} 

public class SecondKind 
{ 
     private readonly string value; 
     public SecondKind (string v) { this.value = v }; 
     public static implicit operator string(SecondKind v){ return v.value; } 
} 
+1

Er hat schon ... – Servy

+0

Versehentlich gepostet, bevor ich fertig war. – Fr33dan

+0

Er macht das auch schon ... – Servy

0

Sie können fast Ihre generische Klasse verwenden. Nur das implizite Umwandeln von Zeichenfolgen (oder anderen Typen) in die abgeleitete Klasse kann niemals funktionieren, weil die Basisklasse nicht wissen kann, ob die Zeichenfolge in das erste oder zweite Zeichen umgewandelt werden soll. So dass ein Teil kopiert werden soll, kann der Rest jedoch in der Basisklasse bleiben:

private class FirstKind : KindOf<string> 
{ 
    public static implicit operator FirstKind(string value) { return new FirstKind{Value = value}; } 
} 
private class SecondKind : KindOf<string> 
{ 
    public static implicit operator SecondKind(string value) { return new SecondKind{Value = value}; } 
} 

auf die Basisklasse Casting kann noch innerhalb der allgemeinen Klasse erfolgen:

 FirstKind Foo = "Foo"; //via implicit operator in FirstKind 
     string stringFoo = Foo; //via implicit operator in baseclass 
+0

Das Problem, das bleibt, ist jedoch, dass Sie etwas wie 'Foo.Length' nicht tun können. Sie müssten Ihren gesamten Code reparieren, um Dinge wie 'Foo.Value.Length' zu tun, die wahrscheinlich nicht den ganzen Aufwand wert sind. –

+0

Ja, das wiederum würde eine dazwischenliegende KindOfString-Klasse erfordern, die von KindOf erbt und diese Eigenschaften implementiert, von denen die anderen Arten erben (oder wenn nur Strings verwendet werden, die generische Klasse allogether überspringen). –

3

ehrlich zu sein, Ich würde die impliziten Operatoren überhaupt nicht oder irgendeine Vererbung für diese Angelegenheit verwenden. Die ganze Idee ist, eine Methode zu haben, die ein FirstKind nimmt. Mit dem impliziten Operator können Sie einfach eine Zeichenfolge übergeben und sie wird konvertiert. Das bedeutet, dass Sie den Code nicht zwingen, explizit anzugeben, dass es sich um eine erste/zweite Art handelt. Ich würde bei zwei Klassen bleiben, die jeweils nur einen Konstruktor haben, der eine Zeichenfolge und eine Nur-Lese-Eigenschaft nimmt. Du machst es zu kompliziert, indem du noch etwas hinzufügst.

Wenn diese Eigenschaft verwendet, ist immer ärgerlich, konnte ich sehen, möglicherweise die implizite Konvertierung - Zeichenfolge, die von jedem der benutzerdefinierten Klassen, aber ich glaube, dass eine implizite Umwandlung von String schädlich wäre . Während ich zustimme, dass Copy/Paste-Code generell zu vermeiden ist, wenn es buchstäblich eine einzige Codezeile ist, kopiere-einfügen, dass die implizite Konvertierung in beide Klassen einfacher, effektiver und im Allgemeinen wartungsfreundlicher ist als der Versuch, Vererbung oder irgendetwas anderes zu verwenden um zu vermeiden, es zweimal zu schreiben.

+0

+1 Leider, da 'String' versiegelt ist, habe ich Angst, dass dies wahrscheinlich die beste Option ist. –

+0

Wir sind uns einig. "Implizite Umwandlung in String" war die Idee, die hinter meiner steht, aber dabei die Klassen getrennt zu halten, wie Sie es vorschlagen. – Fr33dan

1

Ich werde meinen Kommentar eine Antwort machen, weil ich denke, dass es als eins steht.

Von Kopf bis Kopf müssen Sie nur die impliziten Operatoren und Konstruktoren für die Unterklassen implementieren. Ja, sowas wie Codekopieren, aber zumindest benutzt es keinen verschachtelten Prozess oder Vererbung, wenn die Vererbung wirklich nichts reales kommuniziert (rhetorisch, was ist ein "KindOf" überhaupt und bedeutet es wirklich irgendetwas?) Und ist einfach da Code-Duplizierung zwischen den Klassen zu reduzieren, die nichts gemein außer ähnlicher Syntax (aber verschiedene Verwendungen)

public class FirstKind 
{ 
    public string Value { get; private set; } 

    public FirstKind(string value) 
    { 
     this.Value = value; 
    } 

    public static implicit operator FirstKind(string value) 
    { 
     return new FirstKind(value); 
    } 

    public static implicit operator string(FirstKind value) 
    { 
     return value.Value; 
    } 
} 

auch mögen Sie vielleicht unveränderlich die Klasse in Erwägung ziehen, wenn sie eigentlich sind Strings darzustellen. (Ein weiteres Implementierungsdetail, das möglicherweise auf diese Objekte verweist, sollte nicht von einer wiederverwendeten Klasse KindOf stammen, die in Ihrer Implementierung sie immer änderbar macht)

Es ist nicht viel Verkabelung und lässt Ihren Code offen für Änderungen in der Zukunft (vielleicht möchten Sie eine neue Eigenschaft hinzufügen? Fügen Sie einige Validierungscode?) und die Verbraucher der API ist es wirklich egal, dass alles ein KindOf ist, nur um einige kleinere Code-Duplizierung zu speichern. (Vielleicht können Sie einen Ausschnitt machen Ihnen das Leben ein bisschen leichter zu machen?)

1

In Ihrer Frage, FirstKind und SecondKind sind die Arten des strangartigen Variablen und string ist die Art Argument Ihrer KindOf<T> Klasse.Ein anderer Ansatz (der die Code-Duplizierung vermeidet durch die anderen Antworten erforderlich) ist die Rolle des FirstKind und SecondKind zu drehen, so dass sie leeren „Marker“ Klassen zu machen, die als Typ Argument eines String<TKind> Typ verwendet werden:

public class FirstKind { } 
public class SecondKind { } 

public struct String<TKind> 
{ 
    private readonly string _value; 

    public String(string value) 
    { 
     _value = value; 
    } 

    public override string ToString() 
    { 
     return _value; 
    } 

    public static implicit operator String<TKind>(string s) // see the note below 
    { 
     return new String<TKind>(s); 
    } 

    public static implicit operator string(String<TKind> s) 
    { 
     return s.ToString(); 
    } 

    public static String<TKind> operator +(String<TKind> s1, String<TKind> s2) 
    { 
     return new String<TKind>(s1.ToString() + s2.ToString()); 
    } 

    public int Length 
    { 
     get { return _value.Length; } 
    } 

    // You can add methods like Substring and Trim that delegate to _value. 
} 

jetzt können Sie MyMethod wie folgt erklären:

void MyMethod(String<FirstKind> varOne, String<SecondKind> varTwo) 
{ 
    varOne += varOne; // OK 
    varTwo += varTwo; // OK 
    varOne = varTwo; // ERROR 
} 

Hinweis: ich habe von einem einfachen string in der impliziten Umwandlung Betreiber überlassen. Sie sollten es in Betracht ziehen, es zu entfernen, indem Sie Code erzwingen, um die Art der Zeichenfolge explizit zu identifizieren: new String<FirstKind>("...").

+0

Hm, interessanter Ansatz. Ich muss darüber nachdenken. – Atario

Verwandte Themen