Przekazywanie pojedynczego elementu jako liczby
Czy istnieje wspólny sposób przekazywania pojedynczego elementu typu T
do metody, która oczekuje parametru IEnumerable<T>
? Językiem jest C#, framework w wersji 2.0.
Obecnie używam metody pomocniczej (jest to. Net 2.0, więc mam całą masę metod pomocniczych do rzutowania / rzutowania podobnych do LINQ), ale to wydaje się głupie: {]}
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Innym sposobem byłoby oczywiście utworzenie i wypełnienie List<T>
lub Array
i przekazanie go zamiast IEnumerable<T>
.
[Edit] jako metoda rozszerzenia można go nazwać:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Czy coś mi umknęło?
[Edit2] uznaliśmy, że someObject.Yield()
(jak zasugerował @Peter w komentarzach poniżej) jest najlepszą nazwą dla tej metody rozszerzenia, głównie dla zwięzłości, więc tutaj jest wraz z komentarzem XML, jeśli ktoś chce go złapać:
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;
}
}
15 answers
Twoja metoda pomocnicza jest najczystszym sposobem, IMO. Jeśli podasz listę lub tablicę, wtedy pozbawiony skrupułów fragment kodu może go rzucić I zmienić zawartość, prowadząc do dziwnego zachowania w niektórych sytuacjach. Możesz użyć kolekcji tylko do odczytu, ale prawdopodobnie będzie to wymagało jeszcze więcej owijania. Myślę, że Twoje rozwiązanie jest tak schludne, jak tylko się da.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:49:05
Cóż, jeśli metoda oczekuje IEnumerable
musisz przekazać coś, co jest listą, nawet jeśli zawiera tylko jeden element.
Przechodzenie
new T[] { item }
Jako, że argument powinien wystarczyć myślę
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:45:19
W C# 3.0 można korzystać z systemu.Linq.Klasa "Enumerable": {]}
// using System.Linq
Enumerable.Repeat(item, 1);
Spowoduje to utworzenie nowej liczby IEnumerable, która zawiera tylko twój przedmiot.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-22 19:36:24
W C# 3 (wiem, że powiedziałeś 2), możesz napisać generyczną metodę rozszerzenia, która może sprawić, że składnia będzie trochę bardziej akceptowalna:
static class IEnumerableExtensions
{
public static IEnumerable<T> ToEnumerable<T>(this T item)
{
yield return item;
}
}
Kod Klienta to item.ToEnumerable()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-19 19:58:45
Jestem trochę zaskoczony, że nikt nie zasugerował nowego przeciążenia metody argumentem typu T w celu uproszczenia interfejsu API klienta.
public void DoSomething<T>(IEnumerable<T> list)
{
// Do Something
}
public void DoSomething<T>(T item)
{
DoSomething(new T[] { item });
}
Teraz Twój kod klienta może po prostu to zrobić:
MyItem item = new MyItem();
Obj.DoSomething(item);
Lub z listą:
List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-05 16:15:53
Ta metoda pomocnicza działa dla item lub many.
public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
return items;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 13:51:07
Jak właśnie znalazłem, i widziałem, że użytkownik LukeH zasugerował też, miły prosty sposób na zrobienie tego jest następujący:
public static void PerformAction(params YourType[] items)
{
// Forward call to IEnumerable overload
PerformAction(items.AsEnumerable());
}
public static void PerformAction(IEnumerable<YourType> items)
{
foreach (YourType item in items)
{
// Do stuff
}
}
Ten wzorzec pozwoli Ci wywołać tę samą funkcjonalność na wiele sposobów: pojedynczy element; wiele elementów( oddzielonych przecinkami); tablica; lista; wyliczenie itp.
Nie jestem w 100% pewien skuteczności stosowania metody ilościowej, ale to działa.
Update: Theenumerable function looks pretty efficient! (reference )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:01
Albo (jak wcześniej powiedziano)
MyMethodThatExpectsAnIEnumerable(new[] { myObject });
Lub
MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));
Na marginesie, ostatnia wersja może być również przyjemna, jeśli chcesz mieć pustą listę anonimowego obiektu, np.
var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 13:22:21
To może nie jest lepsze ale jest fajne:
Enumerable.Range(0, 1).Select(i => item);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-23 13:49:42
Jest to o 30% szybsze niż yield
lub Enumerable.Repeat
w przypadku użycia w foreach
ze względu na tę optymalizację kompilatora C# , a w innych przypadkach o tej samej wydajności.
public struct SingleSequence<T> : IEnumerable<T> {
public struct SingleEnumerator : IEnumerator<T> {
private readonly SingleSequence<T> _parent;
private bool _couldMove;
public SingleEnumerator(ref SingleSequence<T> parent) {
_parent = parent;
_couldMove = true;
}
public T Current => _parent._value;
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext() {
if (!_couldMove) return false;
_couldMove = false;
return true;
}
public void Reset() {
_couldMove = true;
}
}
private readonly T _value;
public SingleSequence(T value) {
_value = value;
}
public IEnumerator<T> GetEnumerator() {
return new SingleEnumerator(ref this);
}
IEnumerator IEnumerable.GetEnumerator() {
return new SingleEnumerator(ref this);
}
}
W tym teście:
// Fastest among seqs, but still 30x times slower than direct sum
// 49 mops vs 37 mops for yield, or c.30% faster
[Test]
public void SingleSequenceStructForEach() {
var sw = new Stopwatch();
sw.Start();
long sum = 0;
for (var i = 0; i < 100000000; i++) {
foreach (var single in new SingleSequence<int>(i)) {
sum += single;
}
}
sw.Stop();
Console.WriteLine($"Elapsed {sw.ElapsedMilliseconds}");
Console.WriteLine($"Mops {100000.0 / sw.ElapsedMilliseconds * 1.0}");
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:23
Chociaż jest to przesada dla jednej metody, wierzę, że niektórzy ludzie mogą uznać interaktywne rozszerzenia za przydatne.
Interactive Extensions (Ix) firmy Microsoft zawiera następującą metodę.
public static IEnumerable<TResult> Return<TResult>(TResult value)
{
yield return value;
}
Które można wykorzystać w ten sposób:
var result = EnumerableEx.Return(0);
Ix dodaje nową funkcjonalność, której nie można znaleźć w oryginalnych metodach rozszerzenia Linq, i jest bezpośrednim wynikiem tworzenia Reactive Extensions (Rx).
Myśl, Linq Extension Methods
+ Ix
= Rx
dla IEnumerable
.
Możesz znaleźć zarówno Rx i Ix na CodePlex .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-25 23:00:19
Zgadzam się z komentarzami @ EarthEngine do oryginalnego posta, czyli "AsSingleton" to lepsza nazwa. Zobacz ten wpis w Wikipedii . Następnie z definicji Singletona wynika, że jeśli wartość null zostanie przekazana jako argument, że 'AsSingleton' powinien zwrócić IEnumerable z pojedynczą wartością null zamiast pustego IEnumerable, który rozstrzygnie debatę if (item == null) yield break;
. Myślę, że najlepszym rozwiązaniem jest posiadanie dwóch metod: "AsSingleton" i "AsSingletonOrEmpty"; gdzie w przypadku jeśli wartość null zostanie przekazana jako argument, 'AsSingleton' zwróci pojedynczą wartość null, a 'AsSingletonOrEmpty' zwróci pustą liczbę. Tak:
public static IEnumerable<T> AsSingletonOrEmpty<T>(this T source)
{
if (source == null)
{
yield break;
}
else
{
yield return source;
}
}
public static IEnumerable<T> AsSingleton<T>(this T source)
{
yield return source;
}
Wtedy, te, mniej lub bardziej, być analogiczny do 'pierwszy' i 'FirstOrDefault' metody rozszerzenia na IEnumerable, który po prostu czuje się dobrze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-10 16:35:15
IanG mA dobry post na temat , sugerując EnumerableFrom()
jako nazwę i wspomina, że dyskusja wskazuje, że Haskell i Rx nazywają to Return
. IIRC f # nazywa go również Return . F # ' SSeq
dzwoni do operatora singleton<'T>
.
Kuszące, jeśli jesteś przygotowany na C#-centric jest nazwać to Yield
[nawiązując do yield return
zaangażowany w realizację tego].
Jeśli interesują Cię aspekty perf, James Michael Hare ma zwracający zero lub jeden pozycje post zbyt, który jest wart skanowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-19 12:33:17
Najłatwiej byłoby powiedzieć new T[]{item};
; nie ma składni, aby to zrobić. Najbliższym odpowiednikiem, jaki przychodzi mi do głowy, jest słowo kluczowe params
, ale oczywiście wymaga to dostępu do definicji metody i może być użyte tylko z tablicami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:48:56
Jestem trochę spóźniona na imprezę, ale i tak podzielę się swoją drogą. Mój problem polegał na tym, że chciałem powiązać ItemSource lub widok drzewa WPF z jednym obiektem. Hierarchia wygląda tak:
Projekt > działka(y) > Pokój(y)
Zawsze miał być tylko jeden projekt, ale nadal chciałem pokazać projekt w drzewie, bez konieczności przekazywania kolekcji z tylko tym jednym obiektem, jak niektórzy sugerowali.
Ponieważ można przekazać tylko nieliczne przedmioty jako przedmioty, postanowiłem aby moja klasa była liczniejsza:
public class ProjectClass : IEnumerable<ProjectClass>
{
private readonly SingleItemEnumerator<AufmassProjekt> enumerator;
...
public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator;
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
I odpowiednio utworzyć własny Enumerator:
public class SingleItemEnumerator : IEnumerator
{
private bool hasMovedOnce;
public SingleItemEnumerator(object current)
{
this.Current = current;
}
public bool MoveNext()
{
if (this.hasMovedOnce) return false;
this.hasMovedOnce = true;
return true;
}
public void Reset()
{ }
public object Current { get; }
}
public class SingleItemEnumerator<T> : IEnumerator<T>
{
private bool hasMovedOnce;
public SingleItemEnumerator(T current)
{
this.Current = current;
}
public void Dispose() => (this.Current as IDisposable).Dispose();
public bool MoveNext()
{
if (this.hasMovedOnce) return false;
this.hasMovedOnce = true;
return true;
}
public void Reset()
{ }
public T Current { get; }
object IEnumerator.Current => this.Current;
}
To prawdopodobnie nie jest" najczystsze " rozwiązanie, ale działało dla mnie.
EDIT
Aby podtrzymać zasadę pojedynczej odpowiedzialności jak zauważył @Groo, stworzyłem nową klasę wrapper:
public class SingleItemWrapper : IEnumerable
{
private readonly SingleItemEnumerator enumerator;
public SingleItemWrapper(object item)
{
this.enumerator = new SingleItemEnumerator(item);
}
public object Item => this.enumerator.Current;
public IEnumerator GetEnumerator() => this.enumerator;
}
public class SingleItemWrapper<T> : IEnumerable<T>
{
private readonly SingleItemEnumerator<T> enumerator;
public SingleItemWrapper(T item)
{
this.enumerator = new SingleItemEnumerator<T>(item);
}
public T Item => this.enumerator.Current;
public IEnumerator<T> GetEnumerator() => this.enumerator;
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
Które użyłem w ten sposób
TreeView.ItemSource = new SingleItemWrapper(itemToWrap);
EDIT 2
Poprawiłem błąd metodą MoveNext()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 07:30:57