Null检查不需要实例的扩展方法?

时间:2011-06-01 23:34:02

标签: c# null extension-methods

我最近就这个主题提出了this question,该主题已被关闭为this one的副本 - 与我之前的this one一样。但是,我想在一个角落案例中提出一个更具体的问题,我觉得上述问题或答案中没有一个真正涵盖:

实际上不需要实例的扩展方法应该如何响应空引用?

一个例子(答案显而易见)将是.IsNullOrEmpty()(当然应该只返回空引用上的true),但我相信可能还有其他一些有意义的情况在可能为null的对象上调用扩展方法。

另一个例子可能是我在my other question中概述的方法的变体,如果我们暂时假设foreach循环不会抛出空集合(我知道它确实如此,但想象一下)它不仅仅是一个思想实验。)

这里最好的做法是什么?我们应该检查null并抛出ArgumentNullException,还是取决于案例?如果确实取决于具体情况,我们应该根据什么标准决定做什么?

3 个答案:

答案 0 :(得分:7)

就个人而言,我遇到的问题是扩展方法没有抛出null实例。我知道扩展方法是静态方法的语法糖。但是,它们的使用类似于实例方法。因此,我认为它们应该表现得像是一个实例方法,并在“this”实例为null时抛出。

这对我来说是一个可维护性问题。对于不知道方法是在实际类型上还是扩展方法的代码库的新手,看到像IsNull()这样的方法会令人不安,因为它根本没有任何意义。

所以,这是我的两分钱:一个完整​​的个人观点。

答案 1 :(得分:2)

如果我理解正确,那就是你所说的:

public class MyClass
{
    ....
}

public static class MyClassExtension
{
    public static void MyClassExtensionMethod1(this MyClass obj, string name)
    {
        if(obj == null)
        {
            // what to do here, since obj is not used anywhere in this method
        }

        Console.WriteLine(name);
    }
}

我认为这取决于你的思想流派。

由于MyClassExtensionMethod1根本不使用MyClass对象,因此您可以跳过检查,并跳过通知客户端代码(或用户)。

在进一步讨论之前,我会问自己,如果它没有引用传入的类的实例,那么首先使用这个扩展方法有什么意义呢?

我建议采用以下两种方法之一:

如果您可以修改代码,并且此方法属于MyClass,请在MyClass中创建MyClassExtensionMethod1方法。当然,这意味着实例不会为null,因此您正在改变您的期望:)

public class MyClass
{
    ...

    public MyClassMethod1(string name)
    {
        Console.WriteLine(name);
    }
}

...或者,如果您确实不需要MyClass实例,请将此方法移至另一个类,与MyClass无关:

// not an extension class
public class MyOtherClass
{
    public void MyOtherClassMethod1(string name)
    {
        Console.WriteLine(name);
    }
}

即使您保留了扩展方法(以及是否抛出),我建议您在决定时添加好文档,以备将来参考。

答案 2 :(得分:0)

以下是我有意引入扩展方法来处理null的示例。

public class Foo {
    public int Time { get; set; }
}

public static class ExtensionMethods {
    public static int? GetNullableTime(this Foo foo) {
        if (foo == null)  return null;    // Cast is implicit.
        return foo.Time;
    }
}

当你在一些属性初始化程序中使用它时,它会很好。你告诉我什么看起来更好。

var b1 = new Bar{
    T1 = (foo1 == null) ? (int?)null : foo1.Time,   // Note, cast is *required*
    T2 = (foo2 == null) ? (int?)null : foo2.Time,
    T3 = (foo3 == null) ? (int?)null : foo3.Time,
};

var b2 = new Bar{
    T1 = foo1.GetNullableTime(),
    T2 = foo2.GetNullableTime(),
    T3 = foo3.GetNullableTime(),
};