为什么可为 null 的值类型分配不会因 "Converting null literal or possible null value to non-nullable type." 而失败
Why nullable value type assignment does not fail with "Converting null literal or possible null value to non-nullable type."
在 C#10 中,我试图创建一个有错误或值的结果类型(不使用 monadic 的东西)。
当我尝试使用它时,我期待一个编译器 warning/error 但我什么也没得到。
为什么?
我的 .csproj 有:
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
代码:
public record Error();
public record Result<T> where T : notnull
{
private Result()
{
}
public Error? Error { get; private init; }
public T? Value { get; private init; }
[MemberNotNull(nameof(Error))]
public static implicit operator Result<T>(Error error)
{
return new Result<T> {Error = error};
}
[MemberNotNull(nameof(Value))]
public static implicit operator Result<T>(T result)
{
return new Result<T> {Value = result};
}
[MemberNotNullWhen(false, nameof(Value))]
[MemberNotNullWhen(true, nameof(Error))]
public bool IsError()
{
return Error is not null;
}
}
public record TryResult
{
public void ValueTypes()
{
Result<Guid> result = new Error();
Guid value = result.Value; // no error, why?
if (!result.IsError())
value = result.Value;
}
public void RefTypes()
{
Result<string> result = new Error();
string value = result.Value; // error
if (!result.IsError())
value = result.Value; // OK
}
}
Result<Guid> result = new Error();
var value = result.Value; // no error, why?
result
不为空:它是在上面的行中创建的,通过 Error
的隐式转换,隐式转换 returns a non-null Result<T>
.所以访问result.Value
不会失败。
您的 Value
属性 类型为 T?
(忽略 MemberNotNull
内容——我们将在下一节中讨论)。由于 T
不受约束,因此 T?
表示“如果 T
是引用类型,则它可以为 null;如果 T
是值类型,则 ?
被忽略”。由于 T
是 Guid
,这意味着 属性 Value
的类型只有 Guid
。所以写var value = result.Value
和写Guid value = result.Value
是一样的,没问题。
如果 T
是引用类型(例如 string
),则 Value
属性 的类型将是 string?
,即可为空的字符串.即便如此,编译器仍会推断 var
表示 string?
,因此赋值与写入 string? value = result.Value
相同,不会导致任何可空性错误。
Result<string> result = new Error();
string value = result.Value; // error
这一次,T
是 string
,因此 Value
属性 的类型为 string?
。您正在尝试将其分配给 string
类型的变量,因此出现错误。
编译器似乎无法通过隐式转换从 MemberNotNull
进行流分析,我认为这是有道理的。您传递的 属性 名称适用于 当前实例 上的 属性,但隐式转换是静态方法,因此没有适用的当前实例到.
if (!result.IsError())
value = result.Value; // OK
此处,MemberNotNUll
表现符合预期。
在 C#10 中,我试图创建一个有错误或值的结果类型(不使用 monadic 的东西)。
当我尝试使用它时,我期待一个编译器 warning/error 但我什么也没得到。
为什么?
我的 .csproj 有:
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
代码:
public record Error();
public record Result<T> where T : notnull
{
private Result()
{
}
public Error? Error { get; private init; }
public T? Value { get; private init; }
[MemberNotNull(nameof(Error))]
public static implicit operator Result<T>(Error error)
{
return new Result<T> {Error = error};
}
[MemberNotNull(nameof(Value))]
public static implicit operator Result<T>(T result)
{
return new Result<T> {Value = result};
}
[MemberNotNullWhen(false, nameof(Value))]
[MemberNotNullWhen(true, nameof(Error))]
public bool IsError()
{
return Error is not null;
}
}
public record TryResult
{
public void ValueTypes()
{
Result<Guid> result = new Error();
Guid value = result.Value; // no error, why?
if (!result.IsError())
value = result.Value;
}
public void RefTypes()
{
Result<string> result = new Error();
string value = result.Value; // error
if (!result.IsError())
value = result.Value; // OK
}
}
Result<Guid> result = new Error();
var value = result.Value; // no error, why?
result
不为空:它是在上面的行中创建的,通过 Error
的隐式转换,隐式转换 returns a non-null Result<T>
.所以访问result.Value
不会失败。
您的 Value
属性 类型为 T?
(忽略 MemberNotNull
内容——我们将在下一节中讨论)。由于 T
不受约束,因此 T?
表示“如果 T
是引用类型,则它可以为 null;如果 T
是值类型,则 ?
被忽略”。由于 T
是 Guid
,这意味着 属性 Value
的类型只有 Guid
。所以写var value = result.Value
和写Guid value = result.Value
是一样的,没问题。
如果 T
是引用类型(例如 string
),则 Value
属性 的类型将是 string?
,即可为空的字符串.即便如此,编译器仍会推断 var
表示 string?
,因此赋值与写入 string? value = result.Value
相同,不会导致任何可空性错误。
Result<string> result = new Error();
string value = result.Value; // error
这一次,T
是 string
,因此 Value
属性 的类型为 string?
。您正在尝试将其分配给 string
类型的变量,因此出现错误。
编译器似乎无法通过隐式转换从 MemberNotNull
进行流分析,我认为这是有道理的。您传递的 属性 名称适用于 当前实例 上的 属性,但隐式转换是静态方法,因此没有适用的当前实例到.
if (!result.IsError())
value = result.Value; // OK
此处,MemberNotNUll
表现符合预期。