C#扩展方法:如果为null则设置为新实例

4

我有以下扩展方法来帮助我检查和实例化对象是否为空。前两个方法可以正常工作,但它们并不是非常有用。

    public static bool IsNull<T>(this T t)
    {
        return ReferenceEquals(t, null);
    }

    public static T NewIfNull<T>(this T t, Func<T> createNew)
    {
        if (t.IsNull<T>())
        {
            return createNew();
        }

        return t;
    }

    public static void Ensure<T>(this T t, Func<T> createNew)
    {
        t = t.NewIfNull<T>(createNew);
    }

最终我想要做的事情是类似于

IList<string> foo;
...
foo.Ensure<IList<string>>(() => new List<string>());

然而,Ensure方法无法达到所需的效果,即在foo为null时将其设置为List<string>的实例,并在其他情况下将其设置为自身。

如果您知道如何调整Ensure方法以实现此目的,我将不胜感激。

谢谢,汤姆


你需要在“this T t”参数/参数上使用“ref”修饰符才能实现你想要的效果,但这在C#中不受支持。此外,使用null合并的代码可能比这更容易阅读。 - Alan
4个回答

10
你需要区分对象变量。一个对象永远不能为null,而一个变量的值可以为null。你不是试图改变对象的某些东西(这样会起作用),而是试图改变调用者变量的值。
然而,默认情况下参数是按值传递的,这意味着你的扩展方法会改变参数(在方法内声明的变量),但这对调用者的变量没有影响。通常你可以把参数改为ref来实现按引用传递的语义,但扩展方法不能有refout作为第一个参数。
正如其他人所说,使用空合并运算符(??) 是一个更好的选择。注意,在这个表达式中:
foo = foo ?? new List<string>();

只有在 foo 为空时,才会构建新的列表。除非需要,否则不会评估 ?? 的右操作数。


3

所以你正在尝试复制 null-coalescing 运算符?

foo = foo ?? new List<string>();

"你不能在null上调用方法" 你不能调用真实的实例方法,但是你可以在null上调用扩展方法。话虽如此,我也不明白使用扩展方法的意义何在... - BoltClock

1
你的 Ensure 方法不起作用,因为你只是设置了一个本地引用变量 (t),但是没有返回它。如果 Ensure 返回了 t,你可以这样做:
var list2 = list1.Ensure<List<string>>();

然而,你不需要那个,因为你可以使用 ?? 运算符:

var list2 = list1 ?? new List<string>();

0

你的Ensure方法没有做任何事情,因为将新值分配给参数t并不会改变调用者的任何内容,当t不是一个ref(或out)参数时。相反,应该这样说:

IList<string> foo;
...
foo = foo.NewIfNull(() => new List<string>());

这个方法可能可行,但正如其他人所说,这并不是最优美的实现方式。


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