将 Nullable<T> 传递到接受 T 的泛型方法的行为?

问题描述 投票:0回答:1

我想知道将

Nullable<int>
类型传递给接受
T?
(另一种
Nullable<int>
类型)的泛型方法的行为是什么。这是有问题的代码:

int? myNullableParameter = 8;
MyGenericMethod(myNullableParameter);

void MyGenericMethod<T>(T? parameter)
{
    //Do Something with parameter
    Console.WriteLine($"{parameter}");
}

由于编译良好,所以这里存在混乱。我将 myNullableParameter 的类型设置为

Nullable<int>
。然后我将此类型作为
Nullable<T>
传递到参数化泛型方法中。但是,参数的类型也是
T?
,它从编译器解析为
Nullable<T>
。那么它是
Nullable<Nullable<int>>
吗?或者编译器是否只是忽略第二个
Nullable<T>
并将参数保留在方法内作为
Nullable<int>

谢谢!

c# oop nullable
1个回答
0
投票

(我的回答假设您使用的是 C# 9.0 或更高版本)

我想知道将

Nullable<int>
类型传递给接受
T?
(另一种
Nullable<int>
类型)的泛型方法的行为是什么。

我认为你有一个误解;从这里看来,您似乎认为/理解/相信,当 unconstrained 泛型类型

T
注释为
T?
时,这意味着 C#+CLR 会将 值类型
T
类型参数处理为
 Nullable<T>
引用类型
T
类型参数为
[AllowNull]/[MaybeNull]
,包括某种方式编组

然而事实是...事实并非如此.

自 C# 9.0 起,泛型类型语法

T?
T
不受约束时
)仅意味着:

  • 如果
    T
    是引用类型,那么此时,这个T是可能为空,即
    [AllowNull]/[MaybeNull]
  • 如果
    T
    Nullable<U>
    ,那么此时,
    T
    可能
    null
    • nullableStruct.HasValue == false
    • 类似地,如果类型
      T
      not a
      T?
      )出现在同一范围内,则表示非
      null
      Nullable<T>
      (即
      value.HasValue == true
      )。
  • 如果
    T
    是值类型(而不是
    Nullable<U>
    ),则此 T 是 不可为空的
    T
    值类型
    ”。
    • 这个问题非常突出,是 C# 语言上的一个丑陋的新“疣”。 叹息
演示:

考虑这个方法:

void AcceptsAllegedlyNullableT<T>( T? nullableTee, T notNullableTee ) // <-- T is unconstrained here { // etc }

情况 1

:不可空引用类型:如果我们指定 T = String,则

tee
参数保留其
?
注释,因此它变为
String?

所以我们有
    void AcceptsAllegedlyNullableT<String>( String? nullableTee, String notNullableTee )
  • ,这样
    nullableTee
    接受一个 Maybe-
    null
    String
    参考,而
    notNullableTee
    不接受。
    
    
情况 2

:可空引用类型:如果我们指定 T = String?,则

nullableTee
参数
won't
String??,但注释会折叠为相同的
String?
类型。

所以我们有
    void AcceptsAllegedlyNullableT<String?>( String? nullableTee, String? notNullableTee )
  • ,这样
    tee
    接受一个 Maybe-
    null
    String
    引用 - 并且
    notNullableTee
    实际上是可以为空的。
    
    
情况 3

:(不可为 null)-值类型:如果我们指定 T = Int32,那么

?
类型
 上的 
tee
注释是一个谎言,因为它的 静态类型 仍然存在Int32
而不是
Nullable<Int32>

所以我们有
    void AcceptsAllegedlyNullableT<Int32>( Int32 nullableTee, Int32 notNullableTee )
  • nullableTee
    不能
    接受Nullable<Int32>,尽管有
    ?
    注释,但至少
    notNullableTee
    再次被准确命名......
    (我无意中听到人们说,鉴于此,
  • ?
  • 注释应该被解释为“也许-
    default(T)
    ”而不是“也许-
    null
    ” - 但那些人生活在泡沫中)。
    
    
情况 4

:可空值类型:如果我们指定 T = Nullable<Int32>(又名

Int32?
int?
),那么
?
类型上的
nullableTee
注释仍然是谎言
,因为所有 T 参考文献变为 Nullable<T>,而不仅仅是 
T?

所以我们有
void AcceptsAllegedlyNullableT<Nullable<Int32>>( Nullable<Int32> nullableTee, Nullable<Int32> notNullableTee )

  • 并且
    notNullableTee
    是一个
  • 骗子骗子型系统着火
  • 
    
    当您使用这些类型参数参数运行此类程序时,会发生以下情况:
void Main() { AcceptsAllegedlyNullableT<String>( null, "a" ); AcceptsAllegedlyNullableT<String?>( null, null ); AcceptsAllegedlyNullableT<Int32>( 123, 456 ); AcceptsAllegedlyNullableT<Int32?>( null, null ); } static void AcceptsAllegedlyNullableT<T>( T? nullableTee, T notNullableTee ) // <-- T is unconstrained here { Console.WriteLine( "------------------------------------------" ); Type staticType = typeof(T); Type runtimeType1 = nullableTee ?.GetType(); Type runtimeType2 = notNullableTee?.GetType(); Console.WriteLine( "Runtime value of nullableTee : " + ( nullableTee ?.ToString() ?? "null" ) ); Console.WriteLine( "Runtime value of notNullableTee: " + ( notNullableTee?.ToString() ?? "null" ) ); Console.WriteLine( "Static-type of nullableTee : " + typeof(T) .FullName ); Console.WriteLine( "Static-type of notNullableTee: " + typeof(T?).FullName ); Console.WriteLine( "Runtime-type of nullableTee : " + ( nullableTee ?.GetType().FullName ?? "null" ) ); Console.WriteLine( "Runtime-type of notNullableTee: " + ( notNullableTee?.GetType().FullName ?? "null" ) ); Console.WriteLine( "------------------------------------------" ); }

控制台输出:

Runtime value of nullableTee   : null
Runtime value of notNullableTee: a
Static-type of nullableTee   : System.String
Static-type of notNullableTee: System.String
Runtime-type of nullableTee   : null
Runtime-type of notNullableTee: System.String
------------------------------------------
------------------------------------------
Runtime value of nullableTee   : null
Runtime value of notNullableTee: null
Static-type of nullableTee   : System.String
Static-type of notNullableTee: System.String
Runtime-type of nullableTee   : null
Runtime-type of notNullableTee: null
------------------------------------------
------------------------------------------
Runtime value of nullableTee   : 123
Runtime value of notNullableTee: 456
Static-type of nullableTee   : System.Int32
Static-type of notNullableTee: System.Int32
Runtime-type of nullableTee   : System.Int32
Runtime-type of notNullableTee: System.Int32
------------------------------------------
------------------------------------------
Runtime value of nullableTee   : null
Runtime value of notNullableTee: null
Static-type of nullableTee   : System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Static-type of notNullableTee: System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Runtime-type of nullableTee   : null
Runtime-type of notNullableTee: null
------------------------------------------

© www.soinside.com 2019 - 2024. All rights reserved.