C# 中的可空变量是否可以从编译器的某个时间点标记为非空?
Can a nullable variable in C# be marked as non-null from some point on for the compiler?
我使用 null
来表示函数的可选参数 A? a
应该采用在函数中计算的非空默认值。进入函数后,我检查传入的参数a
是否为null
,如果是,则给a
赋一个非空值。从那时起,可以安全地假设 a
是非空的。问题:编译器不知道这一点,现在我必须为函数的其余部分引用 a.Value
,而不是直接的 a
.
有没有办法告诉编译器 a
从某个时候开始实际上是非空的?如果不是,那么处理这种可选参数最清晰的方法是什么?
示例代码:
using System;
namespace test
{
public struct A { public int x; };
class Program
{
static void f(A? a = null)
{
// Assign the default value.
if (a == null) a = new A { x = 3 };
// Now 'a' is non-null for the rest of the function.
// What I do now:
Console.WriteLine(a.Value.x);
// What I'd like to do:
// * Mark 'a' as non-null somehow.
// Now can refer to 'a' directly:
// Console.WriteLine(a.x);
}
static void Main(string[] args)
{
f();
}
}
}
不,不存在 - 不适用于可为 null 的 value 类型。可空值类型的编译时类型仍然是 Nullable<T>
,即使编译器知道它将是非空的。
这与可为空的 reference 类型不同,在后者中,编译器会跟踪它是否认为变量可能为 null 以警告取消引用。对于可为 null 的引用类型,没有“真正的”独立的可为 null 和不可为 null 的类型——只有一个指示值是否为 null 的引用类型。
问题评论中给出的引入新的不可空变量的方法确实是这里前进的唯一途径。
不,这是不可能的。 A?
是 Nullable<A>
的快捷方式。 Nullable<A>
和A
显然是不同的类型,为了得到值,你需要使用Nullable<A>.Value
.
正如@MatthewWatson 评论的那样,您最好的选择是引入另一个变量:
A aValue = a ?? new A { x = 3 };
可以帮助正确命名参数和实际使用的值,以帮助区分它们:
static void f(A? optionalA = null)
{
// Use optionalA if it has a value, or the default value otherwise.
var a = optionalA ?? new A { x = 3 };
看起来您只想允许在不指定参数的情况下调用该方法(并在这种情况下使用特定的默认值)。
对于这种特殊情况还有另一种可能的解决方案:。像这样重载 f()
:
static void f()
{
f(new A { x = 3 });
}
static void f(A a)
{
Console.WriteLine(a.x);
}
代码随后可以调用带参数或不带参数的 f()
,但区别在于它调用带参数的 f(A a)
时,它不能为 null。
(回应您在下方的评论。)
如果你想处理能够调用一个可能为空的结构,你可以像这样重载:
static void f(A? a = default)
{
f(a ?? new A { x = 3 });
}
static void f(A a)
{
Console.WriteLine(a.x);
}
这意味着您的实现不必处理可能为空的值,但您仍然可以在调用站点省略参数,或使用空值调用它。
注意:您可以将 static void f(A? a = default)
简化为:
static void f(A? a = default) => f(a ?? new A { x = 3 });
如果您喜欢更短的语法。
您应该可以为此使用属性。
使用属性,您可以为字段分配新值,并确保这些属性是 return 预期值或默认值。
像这样:
int? a = null;
public int a2
{
get
{
return a.GetValueOrDefault(3);
}
}
那你可以用属性a2
代替a
与将字段从可为 null 更改为不可为 null 不完全相同,但它可能会执行您期望的操作。
我使用 null
来表示函数的可选参数 A? a
应该采用在函数中计算的非空默认值。进入函数后,我检查传入的参数a
是否为null
,如果是,则给a
赋一个非空值。从那时起,可以安全地假设 a
是非空的。问题:编译器不知道这一点,现在我必须为函数的其余部分引用 a.Value
,而不是直接的 a
.
有没有办法告诉编译器 a
从某个时候开始实际上是非空的?如果不是,那么处理这种可选参数最清晰的方法是什么?
示例代码:
using System;
namespace test
{
public struct A { public int x; };
class Program
{
static void f(A? a = null)
{
// Assign the default value.
if (a == null) a = new A { x = 3 };
// Now 'a' is non-null for the rest of the function.
// What I do now:
Console.WriteLine(a.Value.x);
// What I'd like to do:
// * Mark 'a' as non-null somehow.
// Now can refer to 'a' directly:
// Console.WriteLine(a.x);
}
static void Main(string[] args)
{
f();
}
}
}
不,不存在 - 不适用于可为 null 的 value 类型。可空值类型的编译时类型仍然是 Nullable<T>
,即使编译器知道它将是非空的。
这与可为空的 reference 类型不同,在后者中,编译器会跟踪它是否认为变量可能为 null 以警告取消引用。对于可为 null 的引用类型,没有“真正的”独立的可为 null 和不可为 null 的类型——只有一个指示值是否为 null 的引用类型。
问题评论中给出的引入新的不可空变量的方法确实是这里前进的唯一途径。
不,这是不可能的。 A?
是 Nullable<A>
的快捷方式。 Nullable<A>
和A
显然是不同的类型,为了得到值,你需要使用Nullable<A>.Value
.
正如@MatthewWatson 评论的那样,您最好的选择是引入另一个变量:
A aValue = a ?? new A { x = 3 };
可以帮助正确命名参数和实际使用的值,以帮助区分它们:
static void f(A? optionalA = null)
{
// Use optionalA if it has a value, or the default value otherwise.
var a = optionalA ?? new A { x = 3 };
看起来您只想允许在不指定参数的情况下调用该方法(并在这种情况下使用特定的默认值)。
对于这种特殊情况还有另一种可能的解决方案:。像这样重载 f()
:
static void f()
{
f(new A { x = 3 });
}
static void f(A a)
{
Console.WriteLine(a.x);
}
代码随后可以调用带参数或不带参数的 f()
,但区别在于它调用带参数的 f(A a)
时,它不能为 null。
(回应您在下方的评论。)
如果你想处理能够调用一个可能为空的结构,你可以像这样重载:
static void f(A? a = default)
{
f(a ?? new A { x = 3 });
}
static void f(A a)
{
Console.WriteLine(a.x);
}
这意味着您的实现不必处理可能为空的值,但您仍然可以在调用站点省略参数,或使用空值调用它。
注意:您可以将 static void f(A? a = default)
简化为:
static void f(A? a = default) => f(a ?? new A { x = 3 });
如果您喜欢更短的语法。
您应该可以为此使用属性。
使用属性,您可以为字段分配新值,并确保这些属性是 return 预期值或默认值。
像这样:
int? a = null;
public int a2
{
get
{
return a.GetValueOrDefault(3);
}
}
那你可以用属性a2
代替a
与将字段从可为 null 更改为不可为 null 不完全相同,但它可能会执行您期望的操作。