Запутывает ли расширение класса String с помощью IsNullOrEmpty?

Все знают и любят метод String.IsNullOrEmpty (yourString).

Мне было интересно, запутает ли это разработчиков или сделает код лучше, если мы расширим класс String, чтобы он имел такой метод:

yourString.IsNullOrEmpty();

Pro:

  1. Более читабельный.
  2. Меньше набора текста.

Минусы:

  1. Может сбивать с толку, потому что yourString переменная может быть null и похоже, что вы выполняете метод для null переменной.

Что вы думаете?

Тот же вопрос, который мы можем задать о myObject.IsNull() методе.

Вот как я бы это написал:

public static class StringExt
{
  public static bool IsNullOrEmpty(this string text)
  {
    return string.IsNullOrEmpty(text);
  }

  public static bool IsNull(this object obj)
  {
    return obj == null;
  }
}

person Vadim    schedule 26.04.2009    source источник


Ответы (12)


Я лично не фанат этого. Самая большая проблема с методами расширения прямо сейчас - это обнаруживаемость. Если вы не знаете все методы, которые существуют для определенного типа, невозможно взглянуть на вызов метода и узнать, что это метод расширения. Поэтому я считаю проблематичным делать что-либо с помощью метода расширения, что было бы невозможно при обычном вызове метода. В противном случае вы запутаете разработчиков.

Следствие этой проблемы существует в C ++. В некоторых реализациях C ++ можно вызывать методы экземпляра для указателей NULL, если вы не касаетесь каких-либо полей или виртуальных методов в типе. Я работал с несколькими частями кода, которые делают это намеренно и дают методам другое поведение, когда "this==NULL". Работать с этим довольно неприятно.

Это не значит, что мне не нравятся методы расширения. Напротив, мне они нравятся, и я часто ими пользуюсь. Но я думаю, что есть 2 важных правила, которым вы должны следовать при их написании.

  • Относитесь к фактической реализации метода как к еще одному статическому методу, потому что на самом деле это так. Например, выбросить исключение ArgumentException вместо исключения NullReference для нулевого значения this
  • Не позволяйте методу расширения выполнять трюки, которые не может выполнять обычный метод экземпляра

ИЗМЕНИТЬ Ответ на комментарий Мехрдада

Проблема с его использованием в том, что я не считаю, что str.IsNullOrEmpty имеет значительное функциональное преимущество перед String.IsNullOrEmpty (str). Единственное преимущество, которое я вижу, это то, что один требует меньше набора текста, чем другой. То же самое можно сказать о методах расширения в целом. Но в этом случае вы дополнительно меняете то, как люди думают о выполнении программы.

Если люди действительно хотят более короткий набор текста, разве IsNullOrEmpty (str) не будет лучшим вариантом? Он и однозначный, и самый короткий. Настоящий C # сегодня не поддерживает такую ​​возможность. Но представьте, если бы в C # я мог сказать

using SomeNamespace.SomeStaticClass;

В результате все методы SomeStaticClass теперь находятся в глобальном пространстве имен и доступны для привязки. Кажется, это то, что люди хотят, но они прикрепляют это к методу расширения, который я не большой поклонник.

person JaredPar    schedule 26.04.2009
comment
Я согласен с вашим первым пунктом, но не совсем со вторым. Я думаю, что есть проблема несогласованности, которую мы должны решить, и мы ничего не можем с этим поделать. Мы могли бы просто научиться любить это и, по крайней мере, воспользоваться преимуществами (имея представление о том, что на самом деле происходит). Кстати, аналогичная проблема несогласованности возникает в Nullable<T>.Equals() при вызове null. - person mmx; 26.04.2009
comment
Да, nullable сегодня делает подобные трюки. Но посмотрите, сколько людей это смущает. Кажется, примерно раз в неделю у нас появляется всплывающий вопрос о том, почему значения NULL смешно работают с логическими операциями, если одно значение равно NULL. На первую часть отвечу правкой. Закончилось место в поле для комментариев - person JaredPar; 26.04.2009
comment
РЖУ НЕ МОГУ! Этот Nullable был обречен, так как он изменил поведение в бета-версии. Я думаю, что тогда команда SQL / CLR облажалась! Я не говорю конкретно о String.IsNullOrEmpty методе. Каждый метод, который может работать с null приемлемо и имеет смысл как хороший метод расширения. Я считаю, что мы должны согласиться с тем, что синтаксис .Method() - это больше, чем просто вызов метода экземпляра. Например, у нас может быть метод XmlAttribute.ValueEquals (xyz) для обхода нулевой проверки в LINQ to XML. Я не знаю. Я обдумываю вашу точку зрения. Кстати, я ненавижу эту глобальную using штуку. - person mmx; 26.04.2009
comment
Раньше я тоже ненавидел идею глобального импорта, пока не выполнил большое ›управляемое преобразование в VB.Net и C #. VB.Net имеет концепцию глобального импорта, поэтому везде было легко сказать IfFailGo и тому подобное. C # этого не делает, и в этой части кода везде используется NativeMethods.IfFailGo. Через некоторое время это должно действительно раздражать :) - person JaredPar; 26.04.2009
comment
Я думаю, что величие C # заключается в функциях, которые Андерс решил не включать, а не наоборот. :) Поэтому мне не так нравится C # 4.0. Я думаю, что MS пытается исправить проблемы в других частях своей экосистемы с помощью языковых функций. В любом случае хороший ответ, +1 - person mmx; 26.04.2009
comment
Начиная с C # 6, в текущий класс можно импортировать статические методы: using static SomeNamespace.SomeStaticClass - person Episodex; 23.11.2016

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

Позвольте мне опровергнуть их аргументы.

Я ВООБЩЕ не верю, что вызов метода для объекта, который может иметь значение NULL, сбивает с толку. Дело в том, что мы проверяем нули только в определенных местах, а не в 100% случаев. Это означает, что есть процент времени, когда каждый вызов метода, который мы делаем, потенциально связан с нулевым объектом. Это понятно и приемлемо. Если бы это было не так, мы бы проверяли null перед каждым вызовом метода.

Итак, что сбивает с толку, что конкретный вызов метода может происходить с нулевым объектом? Посмотрите на следующий код:

var bar = foo.DoSomethingResultingInBar();
Console.Writeline(bar.ToStringOr("[null]"));

Вы растеряны? Вы должны быть. Потому что вот реализация foo:

public Bar DoSomethingResultingInBar()
{
  return null; //LOL SUCKER!
}

Видеть? Вы читаете образец кода, совершенно не запутавшись. Вы поняли, что потенциально foo вернет null из вызова этого метода, а вызов ToStringOr на bar приведет к NRE. Голова кружилась? Конечно, нет. Понятно, что такое может случиться. Теперь этот метод ToStringOr незнаком. Что вы делаете в таких ситуациях? Вы либо читаете документацию по методу, либо изучаете код вызова. Вот:

public static class BarExtensions
{
  public static string ToStringOr(this bar, string whenNull)
  {
    return bar == null ? whenNull ?? "[null]" : bar.ToString();
  }
}

Сбивает с толку? Конечно, нет. Очевидно, что разработчику нужен сокращенный метод проверки, является ли bar нулевым, и замены его ненулевой строкой. Это может значительно сократить ваш код и повысить удобочитаемость и повторное использование кода. Конечно, вы можете сделать это и другими способами, но этот способ запутывает не больше, чем любой другой. Например:

var bar = foo.DoSomethingResultingInBar();
Console.Writeline(ToStringOr(bar, "[null]"));

Когда вы сталкиваетесь с этим кодом, что у вас должно отличаться от исходной версии? Вам все еще нужно изучить код, вам все равно нужно определить его поведение, когда bar имеет значение null. Вы все еще должны иметь дело с этой возможностью.

Запутывают ли методы расширения? Только если вы их не понимаете. И, откровенно говоря, то же самое можно сказать о ЛЮБОЙ части языка, от делегатов до лямбд.

person Community    schedule 26.04.2009
comment
Правильный способ написать это, если бы .net разрешил это, состоял бы в том, чтобы использовать метод экземпляра на 'bar' и каким-то образом украсить его, чтобы указать компилятору не использовать 'callvirt'. Я считаю подход метода расширения уродливым обходным путем для того, что должно быть указано напрямую. - person supercat; 17.01.2012
comment
@supercat: это не обходной путь, им нужно было реализовать его таким образом, чтобы поддерживать все .NET (и любые другие совместимые с CLS) языки. - person ; 17.01.2012
comment
Я хочу сказать, что спецификация CLS должна была включать способ маркировки методов, которые должны быть вызваны для нулевых экземпляров (а также, между прочим, отмечать, какие методы и свойства структуры изменяют this). Если бы спецификация включала эти вещи, огромный объем кода мог бы быть намного чище. - person supercat; 01.02.2013
comment
... и лучший способ программирования - скрыть ошибки внутреннего состояния, корректно обрабатывая значения NULL, возвращаемые методами, которые не должны возвращать NULL. - person Andrzej H; 11.08.2014
comment
Просто хочу отметить, что анализ кода вызовет CA1062: проверка аргументов общедоступных методов, если к члену обращаются по первому аргументу метода расширения, прежде чем проверять, является ли первый аргумент null. - person DavidRR; 03.05.2016

Я думаю, что корень этой проблемы в том, что Джон Скит упомянул в списке вещей, которые он ненавидит на своем любимом языке (C #): C # не должен был автоматически импортировать все методы расширения во всем пространстве имен. Этот процесс следовало сделать более явным.

Мое личное мнение по этому конкретному вопросу (поскольку мы ничего не можем с этим поделать) использовать метод расширения, если хотите. Я не говорю, что это не сбивает с толку, но этот факт о методах расширения (которые могут быть вызваны по null ссылкам) является глобальным и влияет не только на String.IsNullOrEmpty, поэтому разработчики C # должны с ним ознакомиться.

Кстати, к счастью, Visual Studio четко определяет методы расширения с помощью (extension) во всплывающей подсказке IntelliSense.

person mmx    schedule 26.04.2009
comment
Вопрос, на который вы ссылаетесь, был удален - person Firedragon; 04.02.2014
comment
@Firedragon похоже, что архивная версия все еще доступна здесь: stackprinter.com/questions/ - person Matthias; 16.05.2014
comment
вопрос, на который ссылаются ответ восстановлен, но закрыт. - person DavidRR; 03.05.2016

Мне очень нравится этот подход, ПОКОЛЬКО метод дает понять, что он проверяет, является ли объект нулевым. ThrowIfNull, IsNull, IsNullOrEmpty и т. Д. Он очень удобен для чтения.

person Brian Genisio    schedule 26.04.2009

Лично я бы не стал создавать расширение, которое делает что-то, что уже существует во фреймворке, если бы это не было значительным улучшением удобства использования. В данном случае я не думаю, что это так. Кроме того, если бы мне пришлось создать расширение, я бы назвал его так, чтобы уменьшить путаницу, а не увеличивать ее. Опять же, я считаю, что этот случай не выдерживает проверки.

Сказав все это, у меня есть расширение строки, которое проверяет не только то, является ли строка нулевой или пустой, но также и содержит ли она только пробелы. Я называю это IsNothing. Вы можете найти его здесь.

person tvanfosson    schedule 26.04.2009

Это не просто выглядит так, как будто вы вызываете метод для нулевой переменной. Вы / вызываете / вызываете метод для нулевой переменной, хотя и реализуемый через статический метод расширения. Я понятия не имел, что методы расширения (которых я уже подозревал) поддерживали это. Это даже позволяет делать сумасшедшие вещи, например:

public static int PowerLength(this string obj)
{
return obj == null ? 0 : obj.Length;
}

С того места, где я сейчас стою, я бы отнес любое использование метода расширения к нулевой ссылке как считается вредным.

person Matthew Flaschen    schedule 26.04.2009
comment
Как ни странно, я бы посчитал, что эта функция длины имеет семантику, которую String.Length должна была иметь с самого начала, тем более что в COM и многих языках до .net значение по умолчанию для строковой переменной можно было использовать как пустую строку . Как бы то ни было, в некоторых контекстах значение null интерпретируется как пустая строка, но не в других, что является гораздо более запутанной ситуацией, чем было бы, если бы нулевые объекты, которые были статически типизированы как строки, вели себя как пустые строки. . - person supercat; 17.01.2012

Давайте посмотрим на плюсы и минусы ...

  1. Более читабельный.

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

  1. Меньше набора текста.

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

  1. Может сбивать с толку, потому что переменная yourString может иметь значение NULL и похоже, что вы выполняете метод для переменной NULL.

Верно, (как уже было сказано выше).

person Guffa    schedule 26.04.2009
comment
Я думаю, что главный вопрос заключается в том, считает ли кто-то с философской точки зрения, что BobsStringUtilities.IsNullOrShorterThan(string st, int minLength) больше ассоциируется с BobsStringUtilities или с string. В некоторых случаях указание имени класса перед статической функцией улучшает читаемость, но во многих других случаях это действительно просто шум. - person supercat; 01.02.2013

Да, запутает.

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

Возможно, для кого-то, кто плохо знаком с .NET, с этим методом расширения будет легче справиться, но существует вероятность опасностей, что он / она не понимает всех аспектов методов расширения (например, вам нужно импортировать пространство имен и что его можно вызывать по нулю). Он / она может задаться вопросом, почему этого метода расширения нет в другом проекте. В любом случае: даже если кто-то новичок в .NET, синтаксис метода IsNullOrEmpty() может довольно быстро стать естественным. Также здесь преимущества методов расширения не перевесят вызванную им путаницу.

Изменить: попытался перефразировать то, что я хотел сказать.

person Theo Lenndorff    schedule 26.04.2009
comment
Я не думаю, что мы можем сказать, что она / она не понимает всех аспектов методов расширения, поэтому не используйте это. Если мы будем следовать этой философии, мы можем начать использовать только цикл for, потому что кто-то может не понимать цикл foreach. Моя работа как разработчика - знать язык, на котором я работаю. Вы не будете использовать механика, который не умеет пользоваться своими инструментами. - person Vadim; 26.04.2009
comment
Я не собирался распространять здесь философию. Гипотетически кто-то, плохо знакомый с языком, может счесть синтаксис метода расширения более естественным, но это может длиться недолго, когда он / она узнает, как это работает на самом деле. Это все, что я хотел сказать. - person Theo Lenndorff; 26.04.2009

Я думаю, что расширение любого объекта с помощью вызова типа «IsNull» немного сбивает с толку, и его по возможности следует избегать.

Можно утверждать, что метод IsEmptyString может быть полезен для типа String, и что, поскольку вы обычно комбинируете его с тестом на null, IsNullOrEmpty может быть полезным, но я бы тоже этого избегал из-за того, что тип строки уже есть статический метод, который делает это, и я не уверен, что вы экономите столько текста (максимум 5 символов).

person Martin Peck    schedule 26.04.2009

Вызов метода для переменной, имеющей значение NULL, обычно приводит к исключению NullReferenceException. IsNullOrEmpty()-Method отклоняется от этого поведения таким образом, что невозможно предсказать, просто взглянув на код. Поэтому я бы не советовал использовать его, поскольку он создает путаницу, а выгода от сохранения пары символов минимальна.

person jannmueller    schedule 26.04.2009

В общем, меня устраивает только то, что методы расширения могут безопасно вызывать null, если в их имени есть слово «null» или что-то в этом роде. Таким образом, я понимаю, что вызов с нулевым значением может быть безопасным. Кроме того, они лучше документируют этот факт в своем заголовке XML-комментария, поэтому я получаю эту информацию при наведении указателя мыши на вызов.

person Jonathan Rupp    schedule 26.04.2009

При сравнении экземпляра класса с нулевым значением с помощью ".IsNull ()" даже не короче, чем с использованием "== null". В универсальных классах все меняется, когда аргумент универсального типа без ограничения может быть типом значения. В таком случае сравнение с типом по умолчанию будет долгим, и я использую расширение ниже:

    public static bool IsDefault<T>(this T x)
    {
        return EqualityComparer<T>.Default.Equals(x, default(T));
    }
person too    schedule 09.07.2014