Sowohl in C ++ als auch in C # fehlen einfache Möglichkeiten zum Erstellen eines neuen Typs, der semantisch mit einem vorhandenen Typ identisch ist. Ich finde solche 'typedefs' für die typsichere Programmierung absolut notwendig und es ist eine echte Schande, dass c # sie nicht eingebaut hat. Der Unterschied zwischen void f(string connectionID, string username)
to void f(ConID connectionID, UserName username)
ist offensichtlich ...
(Sie können etwas Ähnliches in C ++ mit Boost in BOOST_STRONG_TYPEDEF erreichen.)
Es mag verlockend sein, Vererbung zu verwenden, aber das hat einige wesentliche Einschränkungen:
- Es funktioniert nicht für primitive Typen
- Der abgeleitete Typ kann weiterhin in den ursprünglichen Typ umgewandelt werden, dh wir können ihn an eine Funktion senden, die unseren ursprünglichen Typ empfängt. Dies macht den gesamten Zweck zunichte
- Wir können nicht von versiegelten Klassen ableiten (und dh viele .NET-Klassen sind versiegelt).
Die einzige Möglichkeit, in C # etwas Ähnliches zu erreichen, besteht darin, unseren Typ in einer neuen Klasse zusammenzustellen:
Class SomeType {
public void Method() { .. }
}
sealed Class SomeTypeTypeDef {
public SomeTypeTypeDef(SomeType composed) { this.Composed = composed; }
private SomeType Composed { get; }
public override string ToString() => Composed.ToString();
public override int GetHashCode() => HashCode.Combine(Composed);
public override bool Equals(object obj) => obj is TDerived o && Composed.Equals(o.Composed);
public bool Equals(SomeTypeTypeDefo) => object.Equals(this, o);
// proxy the methods we want
public void Method() => Composed.Method();
}
Während dies funktioniert, ist es nur für ein typedef sehr ausführlich. Außerdem haben wir ein Problem mit der Serialisierung (dh mit Json), da wir die Klasse über ihre Composed-Eigenschaft serialisieren möchten.
Unten finden Sie eine Hilfsklasse, die das "Curiously Recurring Template Pattern" verwendet, um dies viel einfacher zu machen:
namespace Typedef {
[JsonConverter(typeof(JsonCompositionConverter))]
public abstract class Composer<TDerived, T> : IEquatable<TDerived> where TDerived : Composer<TDerived, T> {
protected Composer(T composed) { this.Composed = composed; }
protected Composer(TDerived d) { this.Composed = d.Composed; }
protected T Composed { get; }
public override string ToString() => Composed.ToString();
public override int GetHashCode() => HashCode.Combine(Composed);
public override bool Equals(object obj) => obj is Composer<TDerived, T> o && Composed.Equals(o.Composed);
public bool Equals(TDerived o) => object.Equals(this, o);
}
class JsonCompositionConverter : JsonConverter {
static FieldInfo GetCompositorField(Type t) {
var fields = t.BaseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
if (fields.Length!=1) throw new JsonSerializationException();
return fields[0];
}
public override bool CanConvert(Type t) {
var fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
return fields.Length == 1;
}
// assumes Compositor<T> has either a constructor accepting T or an empty constructor
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
while (reader.TokenType == JsonToken.Comment && reader.Read()) { };
if (reader.TokenType == JsonToken.Null) return null;
var compositorField = GetCompositorField(objectType);
var compositorType = compositorField.FieldType;
var compositorValue = serializer.Deserialize(reader, compositorType);
var ctorT = objectType.GetConstructor(new Type[] { compositorType });
if (!(ctorT is null)) return Activator.CreateInstance(objectType, compositorValue);
var ctorEmpty = objectType.GetConstructor(new Type[] { });
if (ctorEmpty is null) throw new JsonSerializationException();
var res = Activator.CreateInstance(objectType);
compositorField.SetValue(res, compositorValue);
return res;
}
public override void WriteJson(JsonWriter writer, object o, JsonSerializer serializer) {
var compositorField = GetCompositorField(o.GetType());
var value = compositorField.GetValue(o);
serializer.Serialize(writer, value);
}
}
}
Mit Composer wird die obige Klasse einfach:
sealed Class SomeTypeTypeDef : Composer<SomeTypeTypeDef, SomeType> {
public SomeTypeTypeDef(SomeType composed) : base(composed) {}
// proxy the methods we want
public void Method() => Composed.Method();
}
Außerdem wird der SomeTypeTypeDef
Wille auf die gleiche Weise SomeType
wie bei Json serialisiert .
Hoffe das hilft !