在C#中,是否可以为可为null和不可为null的值编写隐式转换运算符



我正在尝试编写一个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。

如果没有任何封装引用类型的Aliases,那么我认为这里最好的做法是将这里的T限制为structs。之后,TT?成为不同的类型,允许您创建两个运算符:

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()的方法。

最新更新