我正在尝试编写一个Alias
类,它使我能够:
int n = new Count(1);
也就是说,在这种情况下,它将int
封装为Count
,这赋予了一些类型安全性和域意义,同时它自动转换回底层类型。
对于不可为null的引用类型,我有另一个问题。我不知道如何同时处理这两种情况:
int someCount = new Count(1);
Count? nothing = null;
int? noCount = nothing;
发生这种情况是因为我有这样的类型:
record Device(Count Cpu, Count? Cores); // Contrived example
问题似乎是我不能用相同类型的可为null和不可为null的版本重载运算符:
record Alias<T>(T Value)
{
public static implicit operator T(Alias a) => a.Value;
public static implicit operator T?(Alias? a) => null;
}
record Count : Alias<int> { /**/ }
重点是,如果我有一个null,我希望它转换为目标类型的null。
如果没有任何封装引用类型的Alias
es,那么我认为这里最好的做法是将这里的T
限制为struct
s。之后,T
和T?
成为不同的类型,允许您创建两个运算符:
record Alias<T>(T Value) where T: struct
{
public static implicit operator T?(Alias2<T>? a) => a?.Value;
public static implicit operator T(Alias2<T> a) => a.Value;
}
如果您还需要包装引用类型,可以考虑添加另一个仅适用于引用类型的Alias
类型:
record AliasClass<T>(T Value) where T: class
{
[return: NotNullIfNotNull("a")]
public static implicit operator T?(AliasClass<T>? a) => a?.Value;
}
record AliasStruct<T>(T Value) where T: struct
{
public static implicit operator T?(AliasStruct<T>? a) => a?.Value;
public static implicit operator T(AliasStruct<T> a) => a.Value;
}
然后你可以有例如:
record Count(int Value) : AliasStruct<int>(Value) { /**/ }
record StringWrapper(string Value) : AliasClass<string>(Value) { /**/ }
如前所述,不可能用可为null和不可为null的泛型类型重载运算符。
我用扩展方法解决了这个问题:
public static class AliasClass
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : class => a?.Value;
}
public static class AliasStruct
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : struct => a?.Value;
}
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
public abstract record Alias<V, A> where A : Alias<V, A>
{
protected Alias(V value)
{
Value = value;
EnsureValid(Value);
}
public V Value { get; }
protected virtual void EnsureValid(V value) { }
public override sealed string ToString() => Value?.ToString() ?? "";
public static implicit operator V(Alias<V, A> a) => a.Value;
}
用法:
int a = new Count(1);
int? n = new Count(2).Unwrap();
不幸的是,对称性被打破了。对于不可为null的情况,我找不到实现Unwrap()
的方法。