如何在C#的通用方法中返回NULL?

658
我有一个通用方法,其代码如下(是的,我知道IList有谓词,但我的代码不使用IList而是一些其他集合,无论如何,这与问题无关...)
    static T FindThing<T>(IList collection, int id) where T : IThing, new()
    {
        foreach (T thing in collection)
        {
            if (thing.Id == id)
                return thing;
        }
        return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
    }

我遇到了一个构建错误

"不能将null转换为类型参数'T',因为它可能是值类型。考虑使用'default(T)'代替。"

我能避免这个错误吗?


在C# 8中,可空引用类型是否是更好的解决方案?https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references 不管TObject还是intchar,都返回null - Alexander
很遗憾,签名中的 T? 无法工作。@Alexander-ReinstateMonica 我已经阅读了文章,但无论是引用类型还是值类型,我都找不到返回 null 的方法。 - mireazma
1
@mireazma 这个信息太少了,我无法提供帮助。您应该开一个新问题。 - Alexander
我看到的主要问题是你试图使用 null 作为某些地方出现问题的指示。我建议改为抛出异常。这就是异常存在的原因,你可以省略在每个地方编写 if (... != null) 的步骤。 - Markus Safar
13个回答

1143

三种选项:

  • 返回default(或者对于早期版本的C#,是default(T)),这意味着如果T是引用类型(或可空值类型),则返回null;对于int,返回0;对于char,返回'\0'等等。(默认值表格(C#参考)
  • 如果您愿意将T限制为where T : class约束下的引用类型,并像平常一样返回null
  • 如果您愿意将T限制为where T : struct约束下的非空值类型,则再次可以从具有返回值T?的方法中正常返回null - 请注意,这并不是返回空引用,而是可空值类型的空值。

3
如果我的返回类型是枚举而不是类,该怎么办?我不能指定 T: enum :( - Justin
3
在.NET中,枚举是一个非常薄(而且相当泄漏)的整数类型包装器。约定是使用零作为“默认”枚举值。 - Mike Chamberlain
31
我认为这个问题在于,如果你使用通用方法将数据库对象从DbNull转换为Int,并且它返回T的默认值(其中T是int),那么它将返回0。如果这个数字实际上很重要,那么在该字段为空的情况下传递坏数据。或者更好的例子是DateTime。如果该字段类似于“DateClosed”,并且因为帐户仍然处于打开状态而返回null,则实际上会将其默认为1/1/0000,这意味着该帐户在计算机发明之前就关闭了。 - Sinaesthetic
24
所以你应该转换为Nullable<int>Nullable<DateTime>。如果你使用非可空类型并需要表示空值,那么你就是在自找麻烦。 - Jon Skeet
2
我同意,我只是想提出来。我认为我一直在做的更像是MyMethod<T>();,以假设它是一个非可空类型和MyMethod<T?>();,以假设它是一个可空类型。所以在我的情况下,我可以使用一个临时变量来捕获null,然后继续执行。 - Sinaesthetic
自C# 7.1(2017年8月)起,您可以将选项1的语法简化为return default;,因为类型会自动推断。 - Jeppe Stig Nielsen

112
return default(T);

这个链接应该会解释为什么。 - Harper Shelby
1
该死,如果我知道这个关键字的话,我本来可以节省很多时间的。感谢里卡多! - Ana Betts
2
我很惊讶这篇文章没有获得更多的赞,因为“默认”的关键字是一个更综合的解决方案,允许非引用类型与数字类型和结构体一起使用。虽然被接受的答案解决了问题(并且确实有帮助),但它更好地回答了如何将返回类型限制为可空/引用类型的问题。 - Steve Jackson

49
您可以调整约束条件:
where T : class

允许返回 null。


谢谢。我不能选择两个答案作为被接受的解决方案,所以我选择John Skeet的,因为他的答案有两个解决方案。 - edosoft
@Migol 这取决于你的需求。也许他们的项目确实需要它是 IDisposable。是的,大多数情况下不需要。例如,System.String 没有实现 IDisposable。回答者应该澄清这一点,但这并不意味着答案是错误的。 :) - ahwm
@Migol 我不知道为什么我在那里放了IDisposable。已经移除了。 - TheSoftwareJedi

16
以下是您可以使用的两个选项:
return default(T);

或者

where T : class, IThing
 return null;

15
将类约束作为泛型类型的第一个约束添加。
static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

谢谢。我不能选择两个答案作为被接受的解决方案,所以我选择John Skeet的,因为他的答案有两个解决方案。 - edosoft

15
  1. If you have object then need to typecast

    return (T)(object)(employee);
    
  2. if you need to return null.

    return default(T);
    

嗨user725388,请验证第一个选项。 - Jogi Joseph George

7
您的另一个选择是在声明的末尾添加以下内容:
    where T : class
    where T: IList

这样可以让您返回null。


如果两个约束类型相同,您只需提及一次类型并使用逗号,例如 where T : class, IList。如果您有不同类型的约束,则需要重复标记 where,例如 where TFoo : class where TBar : IList - Jeppe Stig Nielsen

6

为了完备起见,需要知道你也可以这样做:

return default;

它返回与return default(T);相同的结果。


5

TheSoftwareJedi的解决方案很有效,

同时您可以使用一些值和可空类型进行存档:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}

3
这是一个关于可空枚举返回值的工作示例:
public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

1
自C# 7.3(2018年5月)开始,您可以将约束改进为where TEnum:struct,Enum。这确保调用者不会意外提供不是枚举类型的值类型(例如intDateTime)。 - Jeppe Stig Nielsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接