2015-05-03 5 views
-1

Если мы хотим, чтобы один IEnumerable<T> представлял собой объединение двух IEnumberable<T> s, мы можем использовать метод LINQ Concat().LINQ Конкатенация с одним дополнительным элементом

Для примера:

int[] a = new int[] { 1, 2, 3 }; 
int[] b = new int[] { 4, 5 }; 

foreach (int i in a.Concat(b)) 
{ 
    Console.Write(i); 
} 

конечно выходов 12345.

Мой вопрос, почему же нет перегрузки Concat() просто принимать один элемент типа T таким образом, что:

int[] a = new int[] { 1, 2, 3 }; 

foreach (int i in a.Concat(4)) 
{ 
    Console.Write(i); 
} 

будет компилировать и производить вывод: 1234?

Гугл по проблеме поднимает пару вопросов, в которых принятый ответ предполагает, что наилучший подход при рассмотрении вопроса заключается в том, чтобы просто сделать a.Concat(new int[] {4}). Какой штраф (МОГ), но немного «нечистым», на мой взгляд, потому что:

  • Может быть, есть падение производительности от объявления нового массива (хотя это, предположительно, будет незначительным довольно много времени Иви)
  • Это просто не выглядит так аккуратно, легко читать и естественно, как a.Concat(4)

Каждый знает, почему такая перегрузка не существует?

Также, предполагая, что мой Google не подведет меня - такого подобного метода расширения LINQ не существует, принимая один элемент типа T.

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

UPDATE:

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

Подробнее Я ищу, чтобы понять ФАКТИЧЕСКИЕ причины, почему это НЕ УЖЕ часть LINQ.

+3

Не может быть причины для упущения. Должна быть веская причина для включения. –

+0

@HenkHolterman Справедливая точка, но ИМХО - пара пулевых точек, о которых я упоминаю, предусматривает эту причину? Нет? –

+0

[_ "Каждая функция начинается в отверстии на 100 пунктов, а это означает, что она должна иметь значительный положительный эффект на общий пакет, чтобы он мог превратиться в язык." _] (Http: // blogs. msdn.com/b/ericgu/archive/2004/01/12/57985.aspx) –

ответ

0

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

Но поскольку LINQ не дошел до .NET 3.0 и реализован в основном с использованием методов расширения, я могу себе представить, что они пропустили методы расширения, работающие над одним элементом T. Тем не менее, это чисто спекуляция с моей стороны.

Однако они включали функции генератора, которые не являются методами расширения. В частности, следующее:

  • Enumerable.Empty
  • Enumerable.Repeat
  • Enumerable.Range

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

int[] a = new int[] { 1, 2, 3 }; 

var myPrependedEnumerable = Enumerable.Repeat(0, 1).Concat(a); 
var myAppendedEnumerable = a.Concat(Enumerable.Repeat(4, 1)); 

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

Enumerable.FromElement(x); // or a better name (see below). 

Отсутствие явной функции единицы любопытно и интересно

В интересном MoreLINQ series сообщений в блоге Барт Де Смет, иллюстрируются с помощью System.Linq.EnumerableEx, пост More LINQ with System.Interactive – Sequences under construction конкретно занимается этим вопросом, используя следующий подходящий метод для построения одного элемента IEnumerable.

public static IEnumerable<TSource> Return<TSource>(TSource value); 

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

Также интересен серия блог Эрик Липперт на monads, который имеет следующую цитату в part eight:

IEnumerable<int> sequence = Enumerable.Repeat<int>(123, 1); 

И, честно говоря, что в прошлом один немного изворотливый. Хотелось бы, чтобы был статический метод на Enumerable специально для создания одноэлементной последовательности.

Кроме того, F # язык предоставляет тип seq:

Последовательности представлены типа seq<'T>, который является псевдонимом для IEnumerable. Поэтому в качестве последовательности может использоваться любой тип .NET Framework, который реализует System.IEnumerable.

Это обеспечивает явную блок функцию как Seq.singleton.

Заключительные

Хотя все это не дает нам факты что пролить свет на причины почему эти конструкции последовательности не явно присутствующие в C#, пока кто-то со знанием акций процесса разработки решения эта информация, она действительно подчеркивает, что стоит знать больше.

+1

_ «Возможно, было приятно, если бы дополнительная перегрузка была включена как синтаксический сахар» - почему? Как 'Enumerable.FromElement (x)' лучше, чем просто 'new [] {x}'? –

+0

@PeterDuniho Я не утверждаю, что это лучше. Было бы ясно, что это намерение: я хочу создать экземпляр 'IEnumerable ' с одним элементом вместо того, чтобы выбирать конкретную реализацию 'IEnumerable ', такую ​​как массив. Мотивы не отличаются от того факта, что я предпочел бы использовать 'IEnumerable.Empty ' вместо 'new int [0];' или 'new int [] {}', (независимо от причин оптимизации статического экземпляра) , Также можно извлечь выгоду из возможных будущих оптимизаций технологий компилятора для одиночных последовательностей элементов, поскольку реализация скрыта от нас. – Alex

+0

@PeterDuniho, я добавил дополнительную информацию в свой ответ, чтобы выяснить, почему это не было бы странно, если бы такая функция генератора существовала, учитывая, что это самый базовый конструктор для монады IEnumerable. – Alex

-2

Философские вопросы, мне это нравится.

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

public static IEnumerable<TSource> Concat(this IEnumerable<TSource> source, TSource element) 
{ 
    return source.Concat(new[]{element}); 
} 

Я думаю, что главный вопрос в том, что IEnumerable непреложный интерфейс и он не предназначен, чтобы быть изменен на лету.

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

Если вам необходимо изменить эту коллекцию, рассмотрите список или другой интерфейс.

+1

Почему объединение одного элемента может быть каким-то иным, чем конкатенирование целой серии, в отношении модификации? –

+0

Боюсь, у меня нет четкого ответа. Единственная причина, по которой я могу найти, состоит в том, что, хотя может случиться, что у вас действительно есть (исключительно) concatenate 2 enumerable, добавление одного элемента никогда не произойдет, потому что IEnumerable является неизменным. Если вы добавляете один элемент в IEnumerable, возможно, вам стоит рассмотреть реализацию IList. Возможно, инженеры Microsoft хотят использовать _underline_ эту концепцию, опуская единственный метод добавления. Имеет смысл для вас? – Vincenzo

0

Во-первых - ваш поисковый запрос в порядке - такого метода нет. Мне нравится идея. Это вариант использования, если вы столкнулись с этим, если бы это было здорово.

Я подозреваю, что он не был включен в LINQ API, потому что дизайнеры не видели достаточно общей потребности в нем. Это только моя гипотеза.

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

public static class EnumerableExtensions { 
    public static IEnumerable<T> Concat<T>(this IEnumerable<T> source, T element) { 
     foreach (var e in source) { 
      yield return e; 
     } 
     yield return element; 
    } 

    public static IEnumerable<T> Concat<T>(this T source, IEnumerable<T> element) { 
     yield return source; 
     foreach (var e in element) { 
      yield return e; 
     } 

    } 
} 

class Program 
{ 
    static void Main() 
    { 
     List<int> ints = new List<int> {1, 2, 3}; 
     var startingInt = 0; 

     foreach (var i in startingInt.Concat(ints).Concat(4)) { 
      Console.WriteLine(i); 
     } 
    } 
} 

Выход:

0 
1 
2 
3 
4 
  • Ленивые вычисления
  • Реализовано аналогично встроенному в методах LINQ (они на самом деле верните внутренний итератор вместо прямого ввода)
  • Проверка аргументов не повредит ему
Смежные вопросы