2009-12-17 5 views
17

Vielleicht demonstriere ich meine Ignoranz einiger häufig verwendeter feautre von C# oder dem .NET-Framework, aber ich würde gerne wissen, ob es eine nativ unterstützte Möglichkeit gibt, einen Typalias wie EmailAddress zu erstellen, der aber string aliasiert kann es mit meinen eigenen Methoden wie bool Validate() erweitern?Gibt es eine Möglichkeit, C# stark typisierte Aliase existierender primitiver Typen wie `string` oder` int` zu definieren?

Ich kenne die using x = Some.Type; Aliase, aber diese sind nicht global noch bieten sie Typ Sicherheit, d. H. Man könnte eine gewöhnliche string für die Verwendung von Alias ​​in der aktuellen Datei austauschen. Ich möchte, dass mein EmailAddress ein eigener Typ ist, unabhängig und nicht austauschbar mit dem string Typ, den es beschattet.

Meine aktuelle Lösung besteht darin, public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable Klassen mit einer T4-Vorlage zu erzeugen, die die impliziten String-Konvertierungsoperatoren und andere solche Dinge erzeugt. Dies ist für mich im Moment in Ordnung und gibt mir viel Flexibilität, aber im Hinterkopf scheint es albern, dass ich so viel Code erzeugen muss, um etwas so einfaches wie das Erstellen eines starken Alias ​​zu tun.

Vielleicht ist das nicht anders als mit Code-Generierung, aber ich bin neugierig, wenn andere etwas ähnliches mit ihren Designs versucht haben und was Ihre Erfahrungen waren. Wenn nichts anderes, könnte dies vielleicht ein guter Anwendungsfall für ein solches Alias-Feature in einer hypothetischen zukünftigen Version von C# sein. Vielen Dank!

EDIT: Der eigentliche Wert, den ich daraus haben möchte, ist Typsicherheit mit primitiven Typen, die verschiedene Typen/Formate für Daten darstellen. Zum Beispiel ein EmailAddress und ein SocialSecurityNumber und ein PhoneNumber, von denen alle string als ihren zugrunde liegenden Typ verwenden, die aber nicht austauschbare Typen an und für sich sind. Ich denke, das bringt Ihnen viel mehr lesbaren und selbstdokumentierenden Code, ganz zu schweigen von den zusätzlichen Vorteilen von mehr Methodenüberladung Möglichkeiten, die weniger zweideutig sind.

+0

Warum abstimmen und keinen Kommentar hinterlassen? Kommt schon Leute. – JMD

+1

Es scheint, dass Sie zumindest eine vernünftige C# knoledgde haben, so mag mein Kommentar dumm erscheinen, aber was Sie wollen, heißt "Typhierarchie" und die Jungs, die die String-Klasse codierten, wollten Sie daran hindern, diese "OO-Funktion" zu verwenden String-Klasse versiegelt, deshalb können Sie nicht tun, was Sie wollen. Der beste Ansatz ist, dass Sie jetzt aktiv sind: Machen Sie Ihren eigenen Typ und eine implizite Umwandlung in String. –

+0

@Andre: warum nicht in Betracht ziehen, deinen Kommentar als Antwort zu posten? –

Antwort

4

Wenn Sie sich das .NET Framework-System ansehen. Das nächstgelegene Beispiel ist eine ähnliche wie eine E-Mail-Adresse. In .NET besteht das Muster darin, etwas in eine Klasse zu packen und auf diese Weise Constraints hinzuzufügen.

Das Hinzufügen einer starken Typisierung, die zu einfachen Typen zusätzliche Einschränkungen hinzufügt, ist eine interessante Sprachfunktion, von der ich glaube, dass sie eine funktionale Sprache hat. Ich kann mich nicht mehr an den Namen der Sprache erinnern, mit der Sie Ihren Werten dimensionale Einheiten wie Füße hinzufügen und eine dimensionale Analyse Ihrer Gleichungen durchführen können, um sicherzustellen, dass die Einheiten übereinstimmen.

+0

Das scheint der beste Weg zu sein. Ich bin nicht gut in den funktionalen Sprachen bewandert, aber ich weiß, dass sie einige geschickte System haben, die Sie viel schöner komponieren können, als ein objektorientiertes tut. –

+1

Ich glaube, dass die Sprache Ada die von Ihnen beschriebene Eigenschaft oder eine ähnliche Eigenschaft hat. – AakashM

+0

Go und Haskell haben auch solche Einschränkungen (Ich bin sicher, Go, Haskell, weiß ich nicht gut). – Crisfole

2

Passt die System.Net.Mail.MailAddress Klasse zu Ihren Bedürfnissen, oder zumindest "Hilfe"?

BEARBEITEN: Es ist nicht ausdrücklich IEquatable oder ISerializable, aber Sie könnten leicht genug diese in Ihrem eigenen Wrapper hinzufügen.

+1

Nein, ich wollte eine allgemeine Lösung, nicht den konkreten Fall einer E-Mail-Adresse fragen. Ich mache auch Dinge wie das Erzeugen von int-Wrappern, um stark typisierte Bezeichner für selbstdokumentierende Zwecke und Flexibilität in Methodenüberladungsdefinitionen zu erzeugen. Zum Beispiel habe ich 'Staff GetStaff (StaffID ID)' und 'Staff GetStaff (TeacherID ID)' beide auf der gleichen Schnittstelle definiert. Beide Methoden führen unterschiedliche Abfragen für die zugrunde liegende Datenquelle aus, je nachdem, welcher Kennungstyp bereitgestellt wird. Lehrer ist-ein Mitarbeiter und eine TeacherID mit dem Wert 1 repräsentieren nicht die gleiche Entität wie StaffID mit dem Wert 1. –

+0

Verstanden, ich habe Ihre Frage falsch interpretiert, weil ich nach diesem speziellen Fall gefragt habe. – JMD

4

Einige Hintergrundinformationen darüber, warum string versiegelt ist:

Von http://www.code-magazine.com/Article.aspx?quickid=0501091:

Rory: Hey Jay, du etwas dagegen, wenn ich Ihnen ein paar Fragen stellen? Ich bin schon neugierig über einige Dinge. Zuerst, und wurde dies auf einer der MSDN Ereignisse, die ich diese Woche gemacht, warum ist String versiegelt? Hinweis: Für VB.NET-Programmierer Sealed = NotInheritable.

Jay: Weil wir eine Menge Magie Tricks in String tun, um zu versuchen, und stellen Sie sicher, wir für Dinge wie Vergleiche zu optimieren, sie so schnell wie machen wir können. Also, wir stehlen Bits von Zeigern und anderen Dingen drin, um Dinge zu markieren.Nur zu geben Sie ein Beispiel, und ich wusste nicht, dies als ich begann, aber wenn ein String hat einen Bindestrich oder ein Apostroph in ihm [dann] es anders sortiert, als wenn es hat nur Text drin, und der Algorithmus für sie das Sortieren, wenn Sie einen Bindestrich oder ein Apostroph, wenn Sie tun global-aware Sortierung ist ziemlich kompliziert, so markieren wir tatsächlich , ob die Zeichenfolge hat, dass Art von Verhalten in ihm.

Rory: Also, was Sie sagen, ist, dass in der Zeichenfolge Welt, wenn Sie es nicht Dichtung String haben viel Chaos eine ganze Menge Platz wäre für wreaking wenn Menschen versuchten, Unterklasse es.

Jay: Genau. Es würde das gesamte Layout des Objekts ändern, also würden wir nicht in der Lage sein, die Tricks zu spielen, die wir jene Aufholgeschwindigkeit spielen.

Hier ist der Codeproject Artikel, die Sie wahrscheinlich gesehen haben:

http://www.codeproject.com/KB/cs/expandSealed.aspx

Also ja, impliziter Operator ist die einzige Lösung.

+0

Danke für die Information, dass 'string' versiegelt ist, aber das ist nicht direkt relevant. –

+1

Ich weiß :) Ich versuche nur persönlich herauszufinden, warum die Dinge, die mich scheißen, so sind, wie sie sind. –

+0

Es gibt Zeiten, in denen es hilfreich wäre, einen Typ quasi aus "string" zu erben, selbst wenn der abgeleitete Typ keinen Zugriff auf nicht-öffentliche Member der zugrunde liegenden Klasse hat, noch irgendwelche seiner Member außer Kraft setzt füge irgendwelche Felder hinzu. Wenn solche Typen neue Interfaces hinzufügen könnten - selbst wenn sie keine Member haben könnten, könnte das hilfreich sein, um Dinge wie tief-unveränderliche Interfaces zu definieren (zB wenn man einen Typ 'DeeplyImmutableInt' deklarieren könnte, der sich genau wie ein' int' verhält) aber geerbt "IAmDeeplyImmutable", und ebenso für andere integrierte Typen, könnte man dann ... – supercat

1

Ich denke, ich verstehe nicht, warum Sie beide starke Typen UND implizite String-Konvertierung zur gleichen Zeit haben wollen. Für mich schließt der eine den anderen aus.

Ich habe versucht, das gleiche Problem für INTS zu lösen (Sie erwähnen int im Titel, aber nicht in der Frage). Ich habe festgestellt, dass die Deklaration einer Enumeration eine typsichere Ganzzahl ergibt, die explizit von/nach int umgewandelt werden muss.

aktualisieren

Aufzählungen kann nicht sein für offene Mengen vorgesehen, kann aber immer noch in einer solchen Art und Weise verwendet werden. Dieses Beispiel stammt aus einem Kompilierungsexperiment, um zwischen den ID-Spalten mehrerer Tabellen in einer Datenbank zu unterscheiden:

enum ProcID { Unassigned = 0 } 
    enum TenderID { Unassigned = 0 } 

    void Test() 
    { 
     ProcID p = 0; 
     TenderID t = 0; <-- 0 is assignable to every enum 
     p = (ProcID)3; <-- need to explicitly convert 

     if (p == t) <-- operator == cannot be applied 
      t = -1; <-- cannot implicitly convert 

     DoProc(p); 
     DoProc(t); <-- no overloaded method found 
     DoTender(t); 
    } 

    void DoProc(ProcID p) 
    { 
    } 

    void DoTender(TenderID t) 
    { 
    } 
+0

In diesem Fall habe ich "stark typisiert" gemeint, da es einen eigenen unterschiedlichen Typ außer "String" hat und nicht direkt austauschbar ist. Entschuldigung für den Begriff Überladung :). Enums helfen nicht viel für den "int" - Fall, da sie ihren eigenen Gültigkeitsbereich definieren und nicht für die Verwendung in einer offenen Fallmenge gedacht sind (im Gegensatz zu einem geschlossenen Satz von Aufzählungswerten). –

1

Ich denke, dass Sie Erweiterungsmethoden verwenden möchten. Sie ermöglichen es Ihnen, eine Klassenfunktionalität zu erweitern, ohne einen neuen abgeleiteten Typ zu erstellen.

+2

Erweiterungsmethoden wären attraktiv, wenn ich den eindeutigen Typalias erstellen und die Erweiterungsmethode für den Alias ​​anstelle des Basistyps "Zeichenfolge" definieren könnte. Ich möchte, dass der Compiler die Sicherheitsprüfung eingibt, um sicherzustellen, dass jemand nicht versehentlich versucht, eine EmailAddress in eine PhoneNumber oder etwas Unvorhergesehenes zu kopieren. –

2

Es scheint, Sie haben mindestens eine vernünftige C# knoledgde, so meine Antwort kann dumm scheinen, aber was Sie wollen, heißt "Typ Hierarchie" und die Jungs, die die String-Klasse codiert wollten Sie diese "OO-Funktion" zu verhindern Also haben sie die String-Klasse versiegelt, darum kannst du nicht machen, was du willst. Der beste Ansatz ist, dass Sie jetzt aktiv sind: Machen Sie Ihren eigenen Typ und eine implizite Umwandlung in String.

0

Ich habe diese Klasse gemacht, um identische Bedürfnisse abzudecken. Dieser ist für den Typ „int“ (Ich habe einen für „string“ auch):

public class NamedInt : IComparable<int>, IEquatable<int> 
{ 
    internal int Value { get; } 

    protected NamedInt() { } 
    protected NamedInt(int val) { Value = val; } 
    protected NamedInt(string val) { Value = Convert.ToInt32(val); } 

    public static implicit operator int (NamedInt val) { return val.Value; } 

    public static bool operator ==(NamedInt a, int b) { return a?.Value == b; } 
    public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; } 
    public static bool operator !=(NamedInt a, int b) { return !(a==b); } 
    public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); } 

    public bool Equals(int other) { return Equals(new NamedInt(other)); } 
    public override bool Equals(object other) { 
     if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false; 
     return Equals(new NamedInt(other.ToString())); 
    } 
    private bool Equals(NamedInt other) { 
     if (ReferenceEquals(null, other)) return false; 
     if (ReferenceEquals(this, other)) return true; 
     return Equals(Value, other.Value); 
    } 

    public int CompareTo(int other) { return Value - other; } 
    public int CompareTo(NamedInt other) { return Value - other.Value; } 

    public override int GetHashCode() { return Value.GetHashCode(); } 

    public override string ToString() { return Value.ToString(); } 
} 

Und es in Ihrem Fall zu konsumieren:

public class MyStronglyTypedInt: NamedInt { 
    public MyStronglyTypedInt(int value) : base(value) { 
     // Your validation can go here 
    } 
    public static implicit operator MyStronglyTypedInt(int value) { 
     return new MyStronglyTypedInt(value); 
    } 

    public bool Validate() { 
     // Your validation can go here 
    } 
} 

Wenn Sie in der Lage sein müssen, serialisiert es (Newtonsoft.Json), lass es mich wissen und ich werde den Code hinzufügen.