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


546

我有一个使用此(虚拟)代码的通用方法(是的,我知道IList具有谓词,但是我的代码未使用IList但使用了其他集合,无论如何这与问题无关...)

static T FindThing<T>(IList collection, int id) where T : IThing, new()
{
    foreach T thing in collecion
    {
        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中)将是对此更好的解决方案吗?docs.microsoft.com/en-us/dotnet/csharp/nullable-references返回null无论TObjectintchar
亚历山大–

Answers:


968

两种选择:

  • 返回值default(T),这意味着您将返回nullT是否0int'\0'for char等的引用类型(或可为空的值类型)(默认值表(C#参考)
  • 将T限制为具有where T : class约束的引用类型,然后null正常返回

3
如果我的返回类型是枚举而不是类怎么办?我无法指定T:枚举:(
贾斯汀

1
在.NET中,枚举是围绕整数类型的非常薄的(而且很漏水的)包装器。约定是将零用作“默认”枚举值。
Mike Chamberlain 2012年

27
我认为这样做的问题是,如果您使用这种通用方法来进行说明,请将Database对象从DbNull转换为Int并返回default(T),其中T是int,则它将返回0。如果此数字为实际上有意义,那么在该字段为空的情况下,您将传递坏数据。或更好的例子是DateTime。如果该字段类似于“ DateClosed”,并且由于和帐户仍处于打开状态而返回空值,则该字段实际上默认为(DateTime)为1/1/0000,这意味着在发明计算机之前该帐户已关闭。
Sinaesthetic 2012年

21
@Sinaesthetic:所以您将转换为Nullable<int>Nullable<DateTime>。如果您使用非空类型并且需要表示一个空值,那么您只是在自找麻烦。
乔恩·斯基特

1
我同意,我只是想提出来。我想我一直在做的更像是MyMethod <T>(); 假定它是一个不可为null的类型,并使用MyMethod <T?>(); 假设它是可为空的类型。因此,在我的场景中,我可以使用temp变量来捕获null并从那里去。
Sinaesthetic,2012年

84
return default(T);


1
该死的,如果我知道这个关键字的话,我会节省很多时间-谢谢Ricardo!
安娜·贝茨

1
令我惊讶的是,由于'default'关键字是一种更全面的解决方案,它可以将非引用类型与数字类型和结构结合使用,因此并未获得更多的支持。虽然可接受的答案解决了问题(确实有帮助),但它更好地回答了如何将返回类型限制为可为空/引用类型。
史蒂夫·杰克逊

33

您可以调整约束:

where T : class

然后允许返回null。


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

@Migol取决于您的要求。也许他们的项目确实需要它IDisposable。是的,在大多数情况下并非必须如此。System.String没有实现IDisposable,例如。回答者应该已经澄清了这一点,但这并没有使答案错误。:)
ahwm

@Migol我不知道为什么我在那里有IDisposable。已移除。
TheSoftwareJedi '18

13

将类约束作为第一个约束添加到泛型类型。

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

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

7
  1. 如果您有对象,则需要打字

    return (T)(object)(employee);
  2. 如果需要返回null。

    return default(T);

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

7

以下是您可以使用的两个选项

return default(T);

要么

where T : class, IThing
 return null;

6

您的另一个选择是将其添加到声明的末尾:

    where T : class
    where T: IList

这样,它将允许您返回null。


如果两个约束都属于同一类型,则只需提及一次类型,然后使用逗号即可,例如where T : class, IList。如果您对不同类型有约束,请重复标记where,如中所述where TFoo : class where TBar : IList
杰普·斯蒂格·尼尔森

3

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;
}

1

推荐错误提示...,然后输入user default(T)new T

您必须在代码中添加一个比较,以确保沿着该路线进行匹配是有效的。

否则,可能考虑将输出参数用于“找到匹配项”。


1

这是Nullable Enum返回值的工作示例:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

自C#7.3(2018年5月)起,您可以将约束条件改进为where TEnum : struct, Enum。这样可以确保调用方不会意外提供不是枚举的值类型(例如an int或a DateTime)。
Jeppe Stig Nielsen

0

上述2个答案的另一种选择。如果将返回类型更改为object,则可以返回null,同时强制转换非null的返回值。

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}

缺点:这将要求方法的调用者强制转换返回的对象(在非null情况下),这意味着装箱->性能降低。我对吗?
尖锐的

0

为了完整起见,很高兴知道您也可以这样做:

return default;

它返回与 return default(T);


0

对我来说,这是可行的。问题到底在哪里?

public static T FindThing<T>(this IList collection, int id) where T : IThing, new()
{
    foreach (T thing in collection)
    {
        if (thing.Id == id)
            return thing;
        }
    }

    return null; //work
    return (T)null; //work
    return null as T; //work
    return default(T); //work


}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.