Нулевые проверки методов расширения, которым не нужен экземпляр? - PullRequest
1 голос
/ 02 июня 2011

Я недавно задал этот вопрос по теме, который был закрыт как дубликат этого - как и этот до моего.Тем не менее, я хочу задать более конкретный вопрос по угловому случаю, который, на мой взгляд, ни один из вышеперечисленных вопросов или их ответов действительно не охватывает:

Как следует использовать метод расширения , который не 'действительно ли нужен экземпляр , реагирующий на нулевые ссылки?

Примером (где ответ очевиден) будет .IsNullOrEmpty() (который, конечно, должен просто возвращать true при нулевом значении)ссылка), но я полагаю, что могут быть и другие случаи, когда имеет смысл вызывать метод расширения для объекта, который может быть нулевым.

Другим примером может быть изменение метода, который я описал в myДругой вопрос , если мы на минуту предположим, что цикл foreach не будет генерировать нулевую коллекцию (я знаю, что это так, но представьте, что это не просто мысленный эксперимент).

Что было бы лучшей практикой здесь?Должны ли мы проверять на ноль и бросать ArgumentNullException в любом случае, или это зависит от случая?Если это зависит от конкретного случая, по каким критериям мы должны решить, что делать?

Ответы [ 3 ]

7 голосов
/ 02 июня 2011

Лично у меня проблема с методами расширения, которые не генерируют пустой экземпляр. Я понимаю, что методы расширения являются синтаксическим сахаром для статических методов. Однако их использование аналогично использованию метода экземпляра. Следовательно, я считаю, что они должны вести себя так, как если бы они были методом экземпляра, и выбрасывать, когда экземпляр «this» равен нулю.

Это проблема для меня. Для новичка в кодовой базе, который не знает, относится ли метод к фактическому типу или является методом расширения, такой метод, как IsNull (), может сбить с толку, поскольку он просто не имеет никакого смысла.

Итак, это мои два цента: полное личное мнение.

2 голосов
/ 02 июня 2011

Если я правильно вас понимаю, это то, что вы говорите:

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, сделайте MyClassExtensionMethod1 методом в MyClass.Конечно, это означает, что экземпляр не будет нулевым, поэтому вы меняете свои ожидания:)

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

Даже если вы сохраняете свой метод расширения (и независимо от того, генерирует он или нет), я предлагаю добавить хорошую документацию по вашему решению для дальнейшего использования.

0 голосов
/ 29 августа 2012

Вот пример, где я намеренно ввел метод расширения для работы с 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(),
};
...