Apakah ada cara umum untuk mengirimkan satu item bertipe T
ke metode yang mengharapkan IEnumerable<T>
parameter? Bahasa adalah C #, framework versi 2.0.
Saat ini saya menggunakan metode helper (ini. Net 2.0, jadi saya memiliki sejumlah metode casting / projecting helper yang mirip dengan LINQ), tetapi ini sepertinya konyol:
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Cara lain tentu saja adalah dengan membuat dan mengisi a List<T>
atau a Array
dan meneruskannya daripada IEnumerable<T>
.
[Sunting] Sebagai metode ekstensi itu mungkin dinamai:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Apakah saya melewatkan sesuatu di sini?
[Sunting2] Kami menemukan someObject.Yield()
(seperti yang disarankan @Peter dalam komentar di bawah) sebagai nama terbaik untuk metode ekstensi ini, terutama untuk singkatnya, jadi di sini beserta komentar XML jika ada yang ingin mengambilnya:
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;
}
}
sumber
if (item == null) yield break;
Sekarang Anda berhenti dari melewati nol serta mengambil keuntungan dari pola objek nol (sepele) untukIEnumerable
. (foreach (var x in xs)
menangani kosongxs
saja). Kebetulan, fungsi ini adalah unit monadik untuk daftar monad ituIEnumerable<T>
, dan mengingat cinta-fest monad di Microsoft saya terkejut sesuatu seperti ini tidak dalam kerangka di tempat pertama.AsEnumerable
karena ekstensi bawaan dengan nama itu sudah ada . (KetikaT
mengimplementasikanIEnumerable
, misalnyastring
,.)Yield
? Tidak ada yang mengalahkan singkatnya.left==null
cek di sini. Itu merusak keindahan kode dan menghentikan kode menjadi lebih fleksibel - bagaimana jika suatu hari nanti Anda perlu menghasilkan singleton dengan sesuatu yang bisa nol? Maksud saya,new T[] { null }
tidak sama dengannew T[] {}
, dan suatu hari Anda mungkin perlu membedakan mereka.Jawaban:
Metode pembantu Anda adalah cara terbersih untuk melakukannya, IMO. Jika Anda memasukkan daftar atau array, maka sepotong kode yang tidak bermoral dapat membuangnya dan mengubah isinya, menyebabkan perilaku aneh dalam beberapa situasi. Anda bisa menggunakan koleksi read-only, tetapi itu kemungkinan akan melibatkan lebih banyak pembungkus. Saya pikir solusi Anda serapi itu.
sumber
myDict.Keys.AsEnumerable()
tempatmyDict
bertipeDictionary<CheckBox, Tuple<int, int>>
. Setelah saya mengganti nama metode ekstensi menjadiSingleItemAsEnumerable
, semuanya mulai berfungsi. Tidak dapat mengonversi IEnumerable <Kamus <Kotak centang, System.Tuple <int, int >>. KeyCollection> 'ke' IEnumerable <CheckBox> '. Konversi eksplisit ada (apakah Anda melewatkan gips?) Saya dapat hidup dengan peretasan untuk sementara waktu, tetapi bisakah peretas daya lainnya menemukan cara yang lebih baik?dictionary.Values
memulai. Pada dasarnya tidak jelas apa yang terjadi, tetapi saya sarankan Anda memulai pertanyaan baru tentang hal itu.Nah, jika metode mengharapkan
IEnumerable
Anda harus melewati sesuatu yang merupakan daftar, bahkan jika itu berisi satu elemen saja.lewat
sebagai argumen harus cukup saya pikir
sumber
Di C # 3.0 Anda dapat memanfaatkan kelas System.Linq.Enumerable:
Ini akan membuat IEnumerable baru yang hanya berisi item Anda.
sumber
new[]{ item }
diri saya sendiri, saya hanya berpikir itu adalah solusi yang menarik.Dalam C # 3 (saya tahu Anda mengatakan 2), Anda dapat menulis metode ekstensi generik yang mungkin membuat sintaks sedikit lebih dapat diterima:
kode klien kemudian
item.ToEnumerable()
.sumber
ToEnumerable
untuk menghindari kekacauanSystem.Linq.Enumerable.AsEnumerable
ToEnumerable
metode ekstensi untukIObservable<T>
, jadi nama metode ini juga mengganggu konvensi yang ada. Jujur, saya sarankan tidak menggunakan metode ekstensi sama sekali, hanya karena terlalu generik.Metode pembantu ini berfungsi untuk item atau banyak.
sumber
params
untuk membangun array, jadi kode yang dihasilkan tampak bersih. Belum memutuskan apakah saya suka kurang lebihnew T[]{ item1, item2, item3 };
untuk beberapa item.Saya agak terkejut bahwa tidak ada yang menyarankan kelebihan baru dari metode dengan argumen tipe T untuk menyederhanakan API klien.
Sekarang kode klien Anda bisa melakukan ini:
atau dengan daftar:
sumber
DoSomething<T>(params T[] items)
yang berarti kompiler akan menangani konversi dari satu item ke array. (Ini juga akan memungkinkan Anda untuk mengirimkan beberapa item terpisah dan, sekali lagi, kompiler akan menangani mengonversinya menjadi sebuah array untuk Anda.)Baik (seperti yang telah dikatakan sebelumnya)
atau
Sebagai catatan tambahan, versi terakhir juga bisa menyenangkan jika Anda menginginkan daftar kosong objek anonim, mis
sumber
Seperti yang baru saja saya temukan, dan melihat bahwa pengguna LukeH menyarankan juga, cara sederhana yang bagus untuk melakukan ini adalah sebagai berikut:
Pola ini akan memungkinkan Anda untuk memanggil fungsi yang sama dalam banyak cara: satu item; banyak item (dipisahkan koma); sebuah array; sebuah daftar; enumerasi, dll.
Saya tidak 100% yakin pada efisiensi menggunakan metode AsEnumerable, tetapi itu berhasil.
Pembaruan: Fungsi AsEnumerable terlihat sangat efisien! ( referensi )
sumber
.AsEnumerable()
sama sekali. ArrayYourType[]
sudah mengimplementasikanIEnumerable<YourType>
. Tetapi pertanyaan saya merujuk pada kasus ketika hanya metode kedua (dalam contoh Anda) tersedia, dan Anda menggunakan .NET 2.0, dan Anda ingin mengirimkan satu item.IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}
metode? Seseorang kemudian dapat menggunakannya dengan apa pun yang diharapkanIEnumerable<T>
, untuk sejumlah item diskrit. Kode dasarnya harus seefisien mendefinisikan kelas khusus untuk mengembalikan satu item.Walaupun itu terlalu banyak untuk satu metode, saya percaya beberapa orang mungkin menemukan Ekstensi Interaktif berguna.
Ekstensi Interaktif (Ix) dari Microsoft mencakup metode berikut.
Yang bisa dimanfaatkan seperti:
Ix menambahkan fungsionalitas baru yang tidak ditemukan dalam metode ekstensi Linq asli, dan merupakan hasil langsung dari menciptakan Ekstensi Reaktif (Rx).
Pikirkan,
Linq Extension Methods
+Ix
=Rx
untukIEnumerable
.Anda dapat menemukan Rx dan Ix di CodePlex .
sumber
Ini 30% lebih cepat daripada
yield
atauEnumerable.Repeat
ketika digunakanforeach
karena optimisasi kompiler C # ini , dan kinerja yang sama dalam kasus lain.dalam tes ini:
sumber
new [] { i }
opsi sekitar 3,2 kali lebih cepat dari opsi Anda. Juga pilihan Anda sekitar 1,2 kali lebih cepat daripada ekstensi denganyield return
.SingleEnumerator GetEnumerator()
yang mengembalikan struct Anda. Compiler C # akan menggunakan metode ini (terlihat melalui mengetik bebek) daripada yang antarmuka dan dengan demikian menghindari tinju. Banyak koleksi bawaan memanfaatkan trik ini, seperti Daftar . Catatan: ini hanya akan berfungsi ketika Anda memiliki referensi langsungSingleSequence
, seperti pada contoh Anda. Jika Anda menyimpannya dalam sebuahIEnumerable<>
variabel maka triknya tidak akan berfungsi (metode antarmuka akan digunakan)IanG memiliki postingan yang bagus tentang topik tersebut , menyarankan
EnumerableFrom()
sebagai nama (dan menyebutkan bahwa Haskell dan Rx menyebutnyaReturn
).IIRC F # menyebutnya Kembali juga. F #Seq
memanggil operatorsingleton<'T>
.Menggoda jika Anda siap menjadi C #-sentris adalah menyebutnya
Yield
[menyinggung orang yangyield return
terlibat dalam mewujudkannya].Jika Anda tertarik pada aspek perf, James Michael Hare memiliki nol pengembalian atau satu pos juga yang layak untuk dipindai.
sumber
Ini mungkin tidak lebih baik tetapi agak keren:
sumber
Saya setuju dengan komentar @ EarthEngine ke posting asli, yaitu 'AsSingleton' adalah nama yang lebih baik. Lihat entri wikipedia ini . Kemudian mengikuti dari definisi singleton bahwa jika nilai nol dilewatkan sebagai argumen bahwa 'AsSingleton' harus mengembalikan IEnumerable dengan nilai nol tunggal, bukan IEnumerable kosong yang akan menyelesaikan
if (item == null) yield break;
perdebatan. Saya pikir solusi terbaik adalah memiliki dua metode: 'AsSingleton' dan 'AsSingletonOrEmpty'; di mana, jika null dilewatkan sebagai argumen, 'AsSingleton' akan mengembalikan nilai nol tunggal dan 'AsSingletonOrEmpty' akan mengembalikan IEnumerable yang kosong. Seperti ini:Kemudian, ini akan, lebih atau kurang, analog dengan metode ekstensi 'First' dan 'FirstOrDefault' pada IEnumerable yang terasa benar.
sumber
Cara termudah yang saya katakan adalah
new T[]{item};
; tidak ada sintaks untuk melakukan ini. Setara terdekat yang dapat saya pikirkan adalahparams
kata kunci, tetapi tentu saja yang mengharuskan Anda memiliki akses ke definisi metode dan hanya dapat digunakan dengan array.sumber
Kode di atas berguna ketika menggunakan suka
Dalam cuplikan kode di atas tidak ada cek kosong / nol, dan dijamin hanya ada satu objek yang dikembalikan tanpa takut akan pengecualian. Lebih lanjut, karena malas, penutupan tidak akan dilaksanakan sampai terbukti tidak ada data yang sesuai dengan kriteria.
sumber
MyData.Where(myCondition)
kosong, itu sudah mungkin (dan sederhana) denganDefaultIfEmpty()
:var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();
. Itu dapat lebih disederhanakanvar existingOrNewObject = MyData.FirstOrDefault(myCondition);
jika Anda inginkandefault(T)
dan bukan nilai khusus.Saya agak terlambat ke pesta, tapi saya tetap akan berbagi jalan. Masalah saya adalah bahwa saya ingin mengikat ItemSource atau WPF TreeView ke objek tunggal. Hirarki terlihat seperti ini:
Proyek> Plot (s)> Kamar
Selalu akan ada hanya satu Proyek tetapi saya masih ingin Tampilkan proyek di Pohon, tanpa harus melewati Koleksi dengan hanya satu objek di dalamnya seperti beberapa yang disarankan.
Karena Anda hanya dapat melewatkan objek IEnumerable sebagai ItemSource, saya memutuskan untuk membuat kelas IEnumerable saya:
Dan buat Enumerator saya sendiri sesuai:
Ini mungkin bukan solusi "terbersih" tetapi itu berhasil untuk saya.
EDIT
Untuk menegakkan prinsip tanggung jawab tunggal seperti yang ditunjukkan @Groo, saya membuat kelas pembungkus baru:
Yang saya gunakan seperti ini
EDIT 2
Saya memperbaiki kesalahan dengan
MoveNext()
metode ini.sumber
SingleItemEnumerator<T>
kelas masuk akal, tapi membuat kelas "item tunggalIEnumerable
dari dirinya sendiri" tampaknya seperti pelanggaran terhadap prinsip tanggung jawab tunggal. Mungkin itu membuatnya lebih praktis, tetapi saya masih lebih suka membungkusnya sesuai kebutuhan.Untuk diajukan di bawah "Tidak selalu solusi yang baik , tetapi masih ... solusi" atau "Trik LINQ bodoh", Anda dapat menggabungkan
Enumerable.Empty<>()
denganEnumerable.Append<>()
...... atau
Enumerable.Prepend<>()
...Dua metode terakhir tersedia sejak .NET Framework 4.7.1 dan .NET Core 1.0.
Ini adalah solusi yang bisa diterapkan jika satu orang benar-benar berniat menggunakan metode yang ada alih-alih menulis mereka sendiri, meskipun aku ragu-ragu jika ini lebih atau kurang jelas dibandingkan dengan
Enumerable.Repeat<>()
solusi . Ini jelas merupakan kode yang lebih panjang (sebagian karena tipe inference parameter tidak dimungkinkanEmpty<>()
) dan membuat objek enumerator dua kali lebih banyak.Akhiri ini dengan "Anda tahu metode ini ada?" jawabannya,
Array.Empty<>()
bisa digantiEnumerable.Empty<>()
, tetapi sulit untuk berdebat yang membuat situasi ... lebih baik.sumber
Saya baru-baru ini menanyakan hal yang sama pada posting lain ( Apakah ada cara untuk memanggil metode C # yang membutuhkan IEnumerable <T> dengan nilai tunggal? ... dengan benchmarking ).
Saya ingin orang-orang mampir di sini untuk melihat perbandingan benchmark singkat yang ditunjukkan di pos baru untuk 4 pendekatan yang disajikan dalam jawaban ini.
Tampaknya hanya menulis
new[] { x }
dalam argumen untuk metode ini adalah solusi terpendek dan tercepat.sumber