Есть ли общий способ передачи одного элемента типа T
в метод, который ожидает IEnumerable<T>
параметр? Язык - C #, версия фреймворка 2.0.
В настоящее время я использую вспомогательный метод (это .Net 2.0, поэтому у меня есть целая куча вспомогательных методов приведения / проектирования, похожих на LINQ), но это просто кажется глупым:
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Другой способ, конечно, состоит в том, чтобы создать и заполнить List<T>
или Array
передать и передать его вместо IEnumerable<T>
.
[Edit] В качестве метода расширения его можно назвать:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Я что-то здесь упускаю?
[Edit2] Мы нашли someObject.Yield()
(как предложил @Peter в комментариях ниже) лучшее имя для этого метода расширения, в основном для краткости, поэтому здесь, наряду с комментарием XML, если кто-то захочет его взять:
public static class IEnumerableExt
{
/// <summary>
/// Wraps this object instance into an IEnumerable<T>
/// consisting of a single item.
/// </summary>
/// <typeparam name="T"> Type of the object. </typeparam>
/// <param name="item"> The instance that will be wrapped. </param>
/// <returns> An IEnumerable<T> consisting of a single item. </returns>
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
}
if (item == null) yield break;
теперь вам не разрешено передавать значение null, а также использовать (тривиальный) шаблон нулевого объекта дляIEnumerable
. (foreach (var x in xs)
справляется с пустымxs
просто отлично). Между прочим, эта функция является монадической единицей для монады списка, которая естьIEnumerable<T>
, и, учитывая монаду love-fest в Microsoft, я удивлен, что что-то подобное не входит в рамки в первую очередь.AsEnumerable
потому что встроенное расширение с таким именем уже существует . (КогдаT
реализуетIEnumerable
, например,.string
)Yield
? Ничто не сравнится с краткостью.left==null
чек здесь. Это нарушает красоту кода и делает его более гибким - что если однажды вам понадобится синглтон с чем-то, что может быть нулевым? Я имею в виду,new T[] { null }
не то же самоеnew T[] {}
, и когда-нибудь вам, возможно, придется различать их.Ответы:
Ваш вспомогательный метод - самый чистый способ сделать это, ИМО. Если вы передадите список или массив, то недобросовестный кусок кода может привести к его преобразованию и изменению содержимого, что в некоторых ситуациях приведет к странному поведению. Вы можете использовать коллекцию только для чтения, но это, вероятно, потребует еще большей упаковки. Я думаю, что ваше решение так же аккуратно, как и получается.
источник
myDict.Keys.AsEnumerable()
гдеmyDict
был типDictionary<CheckBox, Tuple<int, int>>
. После того как я переименовал метод расширения вSingleItemAsEnumerable
, все начало работать. Невозможно преобразовать IEnumerable <Dictionary <CheckBox, System.Tuple <int, int >>. KeyCollection> 'в' IEnumerable <CheckBox> '. Существует явное преобразование (вам не хватает актеров?) Я могу некоторое время жить с хаком, но могут ли другие силовые хакеры найти лучший способ?dictionary.Values
начать с. В принципе, не ясно, что происходит, но я предлагаю вам начать новый вопрос об этом.Хорошо, если метод ожидает, что
IEnumerable
вы должны передать что-то, что является списком, даже если он содержит только один элемент.прохождение
в качестве аргумента должно быть достаточно, я думаю,
источник
В C # 3.0 вы можете использовать класс System.Linq.Enumerable:
Это создаст новый IEnumerable, который содержит только ваш элемент.
источник
new[]{ item }
себя, я просто подумал, что это интересное решение.В C # 3 (я знаю, что вы сказали 2), вы можете написать общий метод расширения, который может сделать синтаксис немного более приемлемым:
клиентский код тогда
item.ToEnumerable()
.источник
ToEnumerable
избежать путаницыSystem.Linq.Enumerable.AsEnumerable
ToEnumerable
метод расширения дляIObservable<T>
, так что это имя метода также мешает существующим соглашениям. Честно говоря, я бы предложил вообще не использовать метод расширения просто потому, что он слишком общий.Этот вспомогательный метод работает для элемента или многих.
источник
params
создание массива, поэтому полученный код выглядит чисто. Не решил, нравится ли мне это больше или меньше, чемnew T[]{ item1, item2, item3 };
для нескольких предметов.Я немного удивлен, что никто не предложил новую перегрузку метода с аргументом типа T для упрощения клиентского API.
Теперь ваш клиентский код может просто сделать это:
или со списком:
источник
DoSomething<T>(params T[] items)
что означает, что компилятор будет обрабатывать преобразование из одного элемента в массив. (Это также позволит вам передавать несколько отдельных элементов и, опять же, компилятор будет обрабатывать преобразование их в массив для вас.)Либо (как уже было сказано ранее)
или
Как примечание, последняя версия также может быть полезна, если вы хотите пустой список анонимного объекта, например
источник
Как я только что обнаружил и увидел, что пользователь LukeH также предложил, хороший простой способ сделать это заключается в следующем:
Этот шаблон позволит вам вызывать одну и ту же функциональность множеством способов: один элемент; несколько элементов (через запятую); массив; список; перечисление и т. д.
Я не уверен на 100% в эффективности использования метода AsEnumerable, но он действительно работает.
Обновление: функция AsEnumerable выглядит довольно эффективно! ( ссылка )
источник
.AsEnumerable()
вообще. МассивYourType[]
уже реализуетIEnumerable<YourType>
. Но мой вопрос касался случая, когда доступен только второй метод (в вашем примере), и вы используете .NET 2.0, и вы хотите передать один элемент.IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}
метода? Затем можно использовать это с любым ожидаемымIEnumerable<T>
, для любого количества отдельных предметов. Код должен быть по сути таким же эффективным, как и определение специального класса для возврата одного элемента.Хотя для одного метода это излишне, я считаю, что некоторые люди могут найти интерактивные расширения полезными.
Интерактивные расширения (Ix) от Microsoft включают в себя следующий метод.
Который можно использовать так:
Ix добавляет новые функциональные возможности, которых нет в оригинальных методах расширения Linq, и является прямым результатом создания Reactive Extensions (Rx).
Подумайте,
Linq Extension Methods
+Ix
=Rx
дляIEnumerable
.Вы можете найти Rx и Ix на CodePlex .
источник
Это 30% быстрее , чем
yield
илиEnumerable.Repeat
при использовании вforeach
связи с этим C # оптимизации компилятора и того же выступления в других случаях.в этом тесте:
источник
new [] { i }
вариант примерно в 3,2 раза быстрее, чем ваш вариант. Также ваш вариант примерно в 1,2 раза быстрее, чем расширение с помощьюyield return
.SingleEnumerator GetEnumerator()
который возвращает вашу структуру. Компилятор C # будет использовать этот метод (он ищет его с помощью утки) вместо интерфейсных и, таким образом, избегает упаковок. Многие встроенные коллекции используют этот прием, как List . Примечание: это будет работать только тогда, когда у вас есть прямая ссылкаSingleSequence
, как в вашем примере. Если вы сохраните его вIEnumerable<>
переменной, то хитрость не сработает (будет использован метод интерфейса)IanG имеет хороший пост на эту тему , предлагая
EnumerableFrom()
в качестве названия (и упоминает, что Haskell и Rx называют егоReturn
).IIRC F # также называет это Return. F #Seq
звонит операторуsingleton<'T>
.Заманчиво, если вы готовы быть C # -центричным, это назвать это
Yield
[намекая наyield return
вовлеченных в реализацию этого].Если вы заинтересованы в его аспектах, у Джеймса Майкла Хэра также есть сообщение с нулевым или одним сообщением, которое стоит проверить.
источник
Это может быть не лучше, но это круто:
источник
Я согласен с комментариями @ EarthEngine к исходному сообщению о том, что AsSingleton - лучшее название. Смотрите эту запись в Википедии . Тогда из определения синглтона следует, что если в качестве аргумента передается нулевое значение, то AsSingleton должен возвращать IEnumerable с единственным нулевым значением вместо пустого IEnumerable, который бы разрешил
if (item == null) yield break;
спор. Я думаю, что лучшее решение - использовать два метода: «AsSingleton» и «AsSingletonOrEmpty»; где, в случае если в качестве аргумента передается значение NULL, AsSingleton возвращает одно значение NULL, а AsSingletonOrEmpty возвращает пустой объект IEnumerable. Нравится:Тогда они, более или менее, будут аналогичны методам расширения 'First' и 'FirstOrDefault' в IEnumerable, который кажется правильным.
источник
Самый простой способ, которым я бы сказал, был бы
new T[]{item};
; нет синтаксиса, чтобы сделать это. Самым близким эквивалентом, который я могу придумать, являетсяparams
ключевое слово, но, разумеется, оно требует от вас доступа к определению метода и может использоваться только с массивами.источник
Приведенный выше код полезен при использовании как
В приведенном выше фрагменте кода проверка пустых / пустых значений отсутствует, и гарантируется, что будет возвращен только один объект, не опасаясь исключений. Кроме того, поскольку оно ленивое, закрытие не будет выполняться, пока не будет доказано, что существующие данные не соответствуют критериям.
источник
MyData.Where(myCondition)
пусто, то это уже возможно (и проще) сDefaultIfEmpty()
:var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();
. Это может быть еще более упрощено,var existingOrNewObject = MyData.FirstOrDefault(myCondition);
если вы хотите,default(T)
а не пользовательское значение.Я немного опоздал на вечеринку, но все равно поделюсь своим путем. Моя проблема заключалась в том, что я хотел связать ItemSource или WPF TreeView с одним объектом. Иерархия выглядит так:
Проект> Участок (ы)> Комната (ы)
Всегда должен был быть только один проект, но я все же хотел показать проект в дереве, не передавая коллекцию только с одним объектом, как предлагали некоторые.
Поскольку вы можете передавать только объекты IEnumerable как ItemSource, я решил сделать свой класс IEnumerable:
И создайте свой собственный перечислитель соответственно:
Вероятно, это не самое «чистое» решение, но оно сработало для меня.
РЕДАКТИРОВАТЬ
Чтобы поддержать принцип единственной ответственности, как отметил @Groo, я создал новый класс-обертку:
Который я использовал так
РЕДАКТИРОВАТЬ 2
Я исправил ошибку с
MoveNext()
методом.источник
SingleItemEnumerator<T>
Класс имеет смысл, но делает класс «единый элементIEnumerable
сам по себе» , кажется , как нарушение одного принципа ответственности. Возможно, это делает его более практичным, но я все равно предпочитаю оборачивать его по мере необходимости.Чтобы быть поданным под "Не обязательно хорошим решением, но все же ... решением" или "Тупыми хитростями LINQ", вы могли бы объединиться
Enumerable.Empty<>()
сEnumerable.Append<>()
...... или
Enumerable.Prepend<>()
...Последние два метода доступны начиная с .NET Framework 4.7.1 и .NET Core 1.0.
Это осуществимое решение , если один были действительно намерены использовать существующие методы вместо того , чтобы писать свои собственные, хотя я в нерешительности , если это более или менее ясно , чем в
Enumerable.Repeat<>()
растворе . Это определенно более длинный код (отчасти из-за невозможности вывода параметров типаEmpty<>()
), однако он создает в два раза больше объектов перечислителя.Завершая это "Знаете ли вы, что эти методы существуют?" Ответ,
Array.Empty<>()
можно заменитьEnumerable.Empty<>()
, но трудно утверждать, что делает ситуацию ... лучше.источник
Я недавно спросил то же самое в другом посте ( есть ли способ вызвать метод C #, требующий IEnumerable <T> с одним значением? ... с тестированием производительности ).
Я хотел, чтобы люди заглянули сюда, чтобы увидеть краткое сравнение производительности, показанное в этом новом сообщении для 4 подходов, представленных в этих ответах.
Кажется, что простая запись
new[] { x }
аргументов метода - самое короткое и быстрое решение.источник