简体   繁体   中英

Null checks on extension methods that do not need an instance?

I recently asked this question on the subject, which was closed as a duplicate of this one - as was this one before mine. However, I want to ask a more specific question on a corner case that I feel none of the above questions, or their answers, really covers:

How should an extension method that doesn't really need an instance react to null references?

An example (where the answer is obvious) would be the .IsNullOrEmpty() (which of course should just return true on a null reference), but I believe there might be other cases where it makes sense to call an extension method on an object that might be null.

Another example could be a variation of the method I outlined in my other question , if we for a minute assume that the foreach loop will not throw on a null collection (I know it does, but imagine it doesn't just as a thought experiment).

What would be best practice here? Should we check for null and throw ArgumentNullException anyway, or does it depend on the case? If it does depend on the case, on what criteria should we decide what to do?

Personally, I have an issue with extension methods that do not throw on a null instance. I understand that extension methods are syntactic sugar for static methods. However, their use is like that of an instance method. Hence, I believe they should behave as if they were an instance method and throw when the "this" instance is null.

This is a maintainability concern for me. For a newcomer to a code base who does not know whether a method is on the actual type or is an extension method, seeing a method like IsNull() would be disconcerting because it simply doesn't make any sense.

So, that is my two cents: a complete utter personal opinion.

If I am understanding you correctly, this is what you're saying:

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

I think it depends on your school of thought.

Since MyClassExtensionMethod1 doesn't use the MyClass object at all, you can skip the check, and skip notifying client code (or users).

Before going further, I would ask myself, what is the point of having this extension method in the first place, if it doesn't reference an instance of the class passed in?

I would just suggest one of the two following approaches:

If you can modify the code, and this method belongs to MyClass, make MyClassExtensionMethod1 a method in MyClass . Of course, this means that the instance will not be null, so you're changing your expectations:)

public class MyClass
{
    ...

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

...Or, if you really, really don't need the MyClass instance, move this method to another class, independent of MyClass :

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

Even if you keep your extension method (and whether it throws or not), I suggest adding good documentation on your decision, for future reference.

Here's an example of where I intentionally introduced an extension method to deal with 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;
    }
}

Makes it nice when you're using this all over in some property initializer. You tell me what looks better.

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(),
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM