我正在尝试编写一个
Alias
类,它使我能够:
int n = new Count(1);
也就是说,它在本例中将
int
封装为 Count
,这提供了一些类型安全性和域含义,同时它会自动转换回底层类型。
对于不可为空的引用类型,我还有另一个问题。我无法弄清楚如何同时处理这两种情况:
int someCount = new Count(1);
Count? nothing = null;
int? noCount = nothing;
发生这种情况是因为我有这样的类型:
record Device(Count Cpu, Count? Cores); // Contrived example
问题似乎是我无法使用同一类型的可空版本和不可空版本来重载运算符:
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
,那么我认为这里最好的做法是将这里的T
限制为struct
。之后,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) { /**/ }
如评论所述,不可能使用可空和不可空泛型类型重载运算符。
我用扩展方法解决了这个问题:
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();
可悲的是,对称性被打破了。我找不到针对不可为空的情况实现
Unwrap()
的方法。
是的,可以使用
NotNullIfNotNullAttribute
。这是一个示例,其中 SpecialString
需要能够转换为 string
(反之亦然)并且需要支持 null 和非 null 实例:
[return: NotNullIfNotNull(nameof(SpecialString))]
public static implicit operator string?(SpecialString? SpecialString) =>
SpecialString is null ? null : SpecialString.Value;
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator SpecialString?(string? value) =>
value is null ? null : new(value);
有关更多信息,请参阅 MSDN 文章。