Должен ли C # иметь одну конструкцию? (или какой самый чистый способ справиться с нулем или одним случаем) - PullRequest
1 голос
/ 04 ноября 2010

При работе с перечислением, содержащим none, один или несколько элемента, цикл foreach упрощает их перебор, а LINQ Select облегчает проецирование элементов из перечисления.

        foreach (var attr in p.GetCustomAttributes().OfType<A>()) ...

При работе с перечислением, содержащим нет или только один элемента, все немного сложнее.Вы можете использовать тот же цикл foreach, но это не гарантирует, что присутствует только один из элементов, и он может выполняться более одного раза.

Вы можете добавить .Take(1), чтобы дать понять, что вытолько хотите до одного и убедитесь, что цикл выполняется только один раз.Но это не приведет к исключению, если их больше одного.

        foreach (var attr in ...().Take(1)) ...

Вы можете добавить проверку заранее, чтобы убедиться, что есть только один, или, возможно, новый контракт кода для проверки, ноэто довольно грязно:

        Contract.Requires(p..GetCustomAttributes().OfType<A>().Count() < 2);
        foreach (var attr in p.GetCustomAttributes().OfType<A>()) ...

Вы даже можете создать новый метод расширения .ZeroOrOne<T>(), который возвращает первый элемент в перечислении, а затем выдает исключение, если вы запрашиваете больше.

        foreach (var attr in ...().ZeroOrOne()) ...

Или вы можете избавиться от цикла, использовать .SingleOrDefault(), а затем проверить на нулевое значение, которое, безусловно, является самым ясным из всех с точки зрения намерений и гарантирует, что цикл выполняется ноль или один раз, но опять же, это больше кода, это меньше 'декларативный «и он не« составной »:

            var attr =  p.GetCustomAttributes().OfType<A>().SingleOrDefault();
            if (attr != null)
            {
               ...

Итак, на вопрос: что из перечисленного (или других предложений) предпочитают люди и почему? Большинство программистов, привыкших к процедурному коду, естественно предпочитают оператор if, но для тех из вас, кто занимается функциональным программированием, у вас есть лучший подход?

В некоторых других языках есть чистые методы длясправиться с этим, например, шаблоны массивов F # могут соответствовать нулю, одному или нескольким, или не работать.Существует ли случай для конструкции forone на языке C #, который был бы эквивалентен проверке, что в перечислении есть ноль или единица, и затем его выполнению?

[Конкретный пример здесь - получениенастраиваемые атрибуты, где атрибут помечен как недопустимый, но вопрос в целом касается обработки случая ноль / один.]

Ответы [ 2 ]

5 голосов
/ 04 ноября 2010

Очевидно, я выберу SingleOrDefault, потому что:

(1) Это встроенный метод. Мне не нужно писать дополнительные методы самостоятельно. Мистер ZeroOrOne<T>, вы вышли.

(2) Простой пример:

 var attr =  source.SingleOrDefault();
 if (attr != null)
 {
   //do something to attr
 }
 else
 {
   //the source is empty
   //throw an exception or prompt the user or something equivalent
 }

Если вы используете другие решения, боюсь, вам нужно написать еще один оператор if для проверки source.Count(). Так что SingleOrDefault является победителем.

2 голосов
/ 09 ноября 2010

Редактировать:

С этим разъяснением я бы предпочел метод расширения ZeroOrOne<T> внутри цикла. По сути, он будет делать то, что вы сделали с явным тестом для более чем одного. Я немного разорван, должен ли он возвращать IEnumerable<T> или Nullable<T>, хотя. Я думаю, это зависит от того, как вы обычно используете результат.

Старый ответ

Мне кажется, что вы хотите использовать метод расширения .Single вместо .SingleOrDefault.

Вы сказали:

Вы можете добавить .Take (1), чтобы было ясно, что вам нужен только один, и чтобы цикл выполнялся только один раз. Но это не приведет к исключению, если их больше одного.

То, что вы описываете, это .Single. Кроме того, оно короткое и очень четко демонстрирует ваши намерения.

var attr = p.GetCustomAttributes().OfType<A>().Single();
// will throw exception if there is more than one attribute of type A
...