【问题标题】:How can I return NULL from a generic method in C#?如何从 C# 中的泛型方法返回 NULL?
【发布时间】:2010-09-23 01:24:57
【问题描述】:

我有一个使用这个(虚拟)代码的通用方法(是的,我知道 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,无论TObject 还是int 还是char
  • 太糟糕了,签名中的T? 不起作用。 @Alexander-ReinstateMonica 我读过这篇文章,但无论引用或值类型如何,都找不到返回 null 的方法。
  • @mireazma 信息太少,我无法提供帮助。您应该提出一个新问题。
  • 我看到的主要问题是您尝试使用null 来表示那里有问题。我建议改为抛出异常。这就是存在异常的原因,您可以在任何地方省略if (... != null)

标签: c# generics


【解决方案1】:

两种选择:

  • 返回 default(T),这意味着如果 T 是引用类型(或可空值类型),则返回 null0 用于 int'\0' 用于 char 等(@ 987654321@)
  • 将 T 限制为具有 where T : class 约束的引用类型,然后正常返回 null

【讨论】:

  • 如果我的返回类型是枚举而不是类怎么办?我不能指定 T : enum :(
  • 在 .NET 中,枚举是整数类型的一个非常薄(而且相当容易泄漏)的包装器。约定是使用零作为“默认”枚举值。
  • 我认为问题在于,如果您使用这种通用方法,将数据库对象从 DbNull 转换为 Int 并返回 default(T),其中 T 是 int,它将返回 0。如果这个数字实际上是有意义的,那么在该字段为空的情况下,您将传递错误的数据。或者更好的例子是 DateTime。如果该字段类似于“DateClosed”并且由于帐户仍处于打开状态而返回为 null,则它实际上将默认 (DateTime) 为 1/1/0000,这意味着该帐户在发明计算机之前已关闭。
  • @Sinaesthetic:所以你可以改成Nullable&lt;int&gt;Nullable&lt;DateTime&gt;。如果你使用一个不可为空的类型并且需要表示一个空值,那你就是自找麻烦。
  • 我同意,我只是想提出来。我认为我一直在做的更像是 MyMethod();假设它是一个不可为空的类型和 MyMethod();假设它是一个可为空的类型。所以在我的场景中,我可以使用一个临时变量来捕获一个空值并从那里开始。
【解决方案2】:
return default(T);

【讨论】:

  • 此链接:msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx 应该解释原因。
  • 该死,如果我知道这个关键字,我会节省很多时间 - 谢谢 Ricardo!
  • 我很惊讶这没有得到更多的支持,因为“默认”关键字是一个更全面的解决方案,允许将非引用类型与数字类型和结构结合使用。虽然接受的答案解决了问题(并且确实很有帮助),但它更好地回答了如何将返回类型限制为可空/引用类型。
【解决方案3】:

你可以调整你的约束:

where T : class

那么返回null是允许的。

【讨论】:

  • 谢谢。我不能选择 2 个答案作为接受的解决方案,所以我选择 John Skeet 的因为他的答案有两个解决方案。
  • @Migol 这取决于您的要求。也许他们的项目确实需要IDisposable。是的,大多数时候不必如此。例如,System.String 没有实现 IDisposable。回答者应该已经澄清了这一点,但这并不会使答案出错。 :)
  • @Migol 我不知道为什么我在那里有 IDisposable。已删除。
【解决方案4】:

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

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

【讨论】:

  • 谢谢。我不能选择 2 个答案作为接受的解决方案,所以我选择 John Skeet 的因为他的答案有两个解决方案。
【解决方案5】:
  1. 如果你有对象,那么需要进行类型转换

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

    return default(T);
    

【讨论】:

  • 您好 user725388,请验证第一个选项
【解决方案6】:

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

return default(T);

where T : class, IThing
 return null;

【讨论】:

    【解决方案7】:

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

        where T : class
        where T: IList
    

    这样就可以让你返回 null。

    【讨论】:

    • 如果两个约束都属于同一种类型,您只需提及一次类型并使用逗号,例如where T : class, IList。如果您对不同类型有限制,请重复标记where,如where TFoo : class where TBar : IList
    【解决方案8】:

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

    【讨论】:

      【解决方案9】:

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

      return default;
      

      返回和return default(T);一样

      【讨论】:

        【解决方案10】:

        接受错误建议...以及用户default(T)new T

        如果你走那条路,你必须在你的代码中添加一个比较,以确保它是一个有效的匹配。

        否则,可能会考虑“找到匹配”的输出参数。

        【讨论】:

          【解决方案11】:

          这是一个可空枚举返回值的工作示例:

          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。这可确保调用者不会意外提供非枚举的值类型(例如 intDateTime)。
          【解决方案12】:

          因为 IThing is interface 是不可能使用 null 的。因此,您必须使用 default(T) 来确定在调用函数之前定义的实际类型 T 的默认值。

          using System;
          using System.Collections.Generic;
          
          public class Program
          {
              public static void Main()
              {
                  Console.WriteLine("Hello World");
                  
                  IThing x = new List<Thing>().FindThing(1);
                  
              }
          
          }
          
          public static class Ext {
              public static T FindThing<T>(this IList<T> collection, int id) where T : IThing, new()
              {
                  foreach (T thing in collection)
                  {
                      if (thing.Id == id) return (T)thing;
                  }
              
                  //return null; //not work
                  //return (T)null; //not work
                  //return null as T; //not work
                  return default(T); //work
              }
          }
          
          public interface IThing { int Id {get; set;} }
          public class Thing : IThing { public int Id {get;set;}}
          

          【讨论】:

          • 你试过这个代码吗?只有return default(T); 可以工作。
          • @E.Shcherbo 不行。在小提琴上试试。
          • 好的,我没有在你的回答中注意到你的 IThing 是类,所以你的方法知道 T 是类,但我确信问题的作者意味着IThing 是一个接口(@98​​7654326@ 前缀的含义)。
          • @E.Shcherbo 我必须道歉。你有权利。不幸的是,问题不是很冗长。因此对于像我这样的懒人来说,达到同样的条件并不是很可行。 :D
          • 不用担心,尽管您的回答是关于其他概念(类与接口)的,但我仍然认为它很好。它展示了当类型参数已知为类或接口时泛型方法的行为有何不同的重点,并强调了where T : IThing 也适用于值类型这一事实。
          【解决方案13】:

          上面提供的 2 个答案的另一种选择。如果将返回类型更改为object,则可以返回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
          }
          

          【讨论】:

          • 缺点:这将要求方法的调用者强制转换返回的对象(在非空情况下),这意味着装箱 -> 性能较低。我说的对吗?
          猜你喜欢
          • 1970-01-01
          • 2020-09-05
          • 1970-01-01
          • 2012-07-11
          • 1970-01-01
          • 2019-09-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多