如何在C#中返回空值而不是null

4

我有一个字符串数组的扩展,如果数组为空,则返回空字符串,如果数组长度等于1,则返回第一个元素,否则返回索引位置的元素,没有边界检查。

public static Strings IndexOrDefault(this String[] arr, Int32 index)
{
    if (arr == null)
        return String.Empty;
    return arr.Length == 1 ? arr[0] : arr[index];
}

现在,我需要将这个功能扩展到其他数组上,因此我想这个扩展方法可以胜任。
public static T IndexOrDefault<T>(this T[] arr, Int32 index)
{
    if (arr == null)
        return default(T);
    return arr.Length == 1 ? arr[0] : arr[index];
}

除了默认值(default(String))不是String.Empty而是null之外,一切都很好。因此,现在我为字符串返回null,这是第一个要求...是否有替代default的方法可以返回空的通用值而不是null?
编辑2:(第一个是定义)
现在我正在使用这种方法,它有效,但不像我想要的那样优雅(好吧,方法也不太优雅,但解决方案可能会更好:))
public static T IndexOrDefault<T>(this T[] arr, Int32 index)
{
    if (arr == null)
        return default(T);
    return arr.Length == 1 ? arr[0] : arr[index];
}

public static String IndexOrDefault(this String[] arr, Int32 index)
{
    if (arr == null)
        return String.Empty;
    return arr.Length == 1 ? arr[0] : arr[index];
}

有两种不同的方法,一种适用于字符串,另一种是通用的。


2
从名为WhateverOrDefault的方法中返回任何不是null的引用类型将会令人困惑、不直观,而且实际上是个谎言。你的方法说了一件事,但你却做了另一件事。nullstring的默认值,而不是String.Empty。此外,如果长度为1,为什么要通过返回array[0]来吞噬错误?如果调用者传递了无效的索引,那么他们应该知道这一点,而不是花时间(可能)试图找到隐藏的错误。 - Ed S.
是的,你说得对,这段代码是从带有服务器端JS的asp移植过来的,需要在某些地方进行重构...但是,总得有人为此付费 :) - Eugenio Miró
6个回答

3

对于引用类型和某些值类型,除了null和0的某个变体之外,没有通用默认值。

不过您可以轻松提供一个默认值。

public static T IndexOrDefault<T>(this T[] arr, Int32 index, T theDefaultValue)
{
    if (arr == null)
        return theDefaultValue;
    return arr.Length == 1 ? arr[0] : arr[index];
}

public static T IndexOrDefault<T>(this T[] arr, Int32 index, Func<T> defaultFactory)
{
    if (arr == null)
        return defaultFactory();
    return arr.Length == 1 ? arr[0] : arr[index];
}

1
好的回答。两个建议 - 首先,不要将您的参数命名为“default”,因为它是一个保留字。其次,将“T defaultValue”作为可选参数,这样您就可以拥有更好的API,并且只需要在需要覆盖默认值时指定默认值。 - mattmc3
另外需要注意的是,如果您传递对象类型的默认值,则即使您不需要它,也必须新建一个对象,这可能会很昂贵。我想这就是为什么您允许使用defaultFactory替代方案的原因。另一种方法是在调用者中使用合并运算符??,并完全省略方法实现中的默认覆盖。这将是一种更一致和惯用的选项,更符合框架内置的其他...OrDefault()方法的使用方式。 - mattmc3
@mattmc3 感谢你的“default”提示。没错,defaultFactory的实现是为了在创建新的T()费时或不经常需要的情况下使用。我没有为参数提供默认值,因为我不确定OP在使用哪个版本的C#。 - cordialgerm
@mattmc3 使用 ?? 运算符的问题在于,数组可能在索引中存有 null。在这种情况下,你无法知道函数返回的 null 是因为数组为 null 还是因为指定的索引处有 null 值。 - cordialgerm
这个解决方案意味着要因为第三个参数而更改我已经拥有的所有代码,但这在这里不适用。顺便说一句,回答很好,谢谢! - Eugenio Miró

1
或者如果数组在索引位置上没有元素,则返回第一个元素。不完全正确。该方法仅在数组长度为1时返回第一个元素,但它根本不检查索引。您所描述的更像是这样的内容:
public static Strings IndexOrDefault(this String[] arr, Int32 index) {
  if (arr == null || arr.Length == 0) return String.Empty;
  if (index >= arr.Length) return arr[0];
  return arr[index];
}

通用版本如下:

泛型版的代码如下:

public static T IndexOrDefault<T>(this T[] arr, Int32 index) {
  if (arr == null || arr.Length == 0) return default(T);
  if (index >= arr.Length) return arr[0];
  return arr[index];
}

有没有替代默认值的方法,可以返回一个空的通用值而不是 null?
不,没有办法为通用类型获取空值。大多数类型根本没有任何空值。例如,int 不能是空的。

你说得也对,感谢评论,我会重新编写定义,原始代码没有检查,因此被移植为原样。 - Eugenio Miró

0
你可以检查一下"特殊"类型的string。另外,你的代码并没有做你所说的那样 - 我帮你修复了一些边缘情况。
public static T IndexOrDefault<T>(this T[] arr, Int32 index)
{
    if (arr == null || arr.Length == 0)
        return default(T);

    return arr.Length <= index ? arr[0] : arr[index];
}

0
不是,nullstring 的默认值。你需要添加代码来特别测试T是否为字符串,或者使用不同的方法。

0
你可以尝试返回一个new T() - 但是你需要在方法声明上添加一个where T : new的约束条件。
否则,字符串的默认值为null。所以下一个选项是创建一个字符串重载...我知道这不是最好的选择,但你可能会用它来解决问题。

注意,这种方法不适用于值类型,例如 Int... 因此,我个人更愿意测试 T 是否为 String 并显式创建一个 String.Empty 来返回。 - Anže Vodovnik
我现在不在编译器旁边,但是你不能针对值类型做第二个where T : struct覆盖吗? - mattmc3

0

当你返回T的默认值时会发生什么?


您可以在此处查看(http://msdn.microsoft.com/en-us/library/xwth0h0d.aspx),但总结一下:对于引用类型的默认值,default(T)为null;对于数值类型,默认值为0;对于结构体类型,所有值都将根据它们是引用类型还是数值类型而设置为0或null。 - Eugenio Miró
嗯...也许他可以使用activator.createinstance()调用,将其转换为T类型以传回? - Richard B

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