단일 항목을 IEnumberable로 전달
유형의 단일 항목을 전달하는 일반적인 방법이 있습니까?T▁로▁method▁expects으를 예상하는 방법으로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>.
[편집] 확장 방법으로 다음과 같은 이름이 지정될 수 있습니다.
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
내가 뭘 빠트렸나요?
[편집2] 찾았습니다someObject.Yield()가 아래 한 것처럼) 이 방법에 될 (@Peter 아제것처한럼안서석래에) 주로가 것다될니입이적이름합한장에방법위해확장이주결을함간가▁(▁wants(다,것▁it니▁if▁is▁method▁comment▁anyone▁along▁it:▁xml입)as▁bre▁in될▁extension▁topeter▁so▁for,vity▁the▁@▁here▁for▁with▁this▁below▁mainly▁be@▁the이▁comments이peter▁the▁to▁grab▁best▁name적름주한합▁suggested가장아로 따라서 여기에 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;
}
}
음, 만약 그 방법이 예상한다면.IEnumerable당신은 목록인 것을 통과해야 합니다. 비록 그것이 하나의 요소만 포함하더라도 말입니다.
지나가는
new[] { item }
논쟁은 충분해야 하기 때문에 나는 생각합니다.
C# 3.0에서는 시스템을 활용할 수 있습니다.Linq.숫자 클래스:
// using System.Linq
Enumerable.Repeat(item, 1);
이렇게 하면 항목만 포함된 새 IE 번호가 만들어집니다.
당신의 도우미 방법이 가장 깨끗한 방법입니다, IMO.만약 당신이 목록이나 배열을 전달한다면, 비양심적인 코드 조각이 그것을 캐스팅하고 내용을 변경할 수 있고, 어떤 상황에서는 이상한 행동으로 이어질 수 있습니다.읽기 전용 컬렉션을 사용할 수 있지만, 이 경우 포장이 더 많이 필요할 수 있습니다.저는 당신의 해결책이 깔끔하다고 생각합니다.
C# 3(2라고 말한 것으로 알고 있습니다)에서는 구문을 조금 더 수용할 수 있는 일반적인 확장 방법을 작성할 수 있습니다.
static class IEnumerableExtensions
{
public static IEnumerable<T> ToEnumerable<T>(this T item)
{
yield return item;
}
}
클라이언트 코드는 다음과 같습니다.item.ToEnumerable().
이 도우미 방법은 항목 또는 여러 항목에 대해 사용할 수 있습니다.
public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
return items;
}
클라이언트 API를 단순화하기 위해 T형 인수로 메소드의 새로운 오버로드를 제안한 사람이 없다는 것에 약간 놀랐습니다.
public void DoSomething<T>(IEnumerable<T> list)
{
// Do Something
}
public void DoSomething<T>(T item)
{
DoSomething(new T[] { item });
}
이제 클라이언트 코드로 다음 작업을 수행할 수 있습니다.
MyItem item = new MyItem();
Obj.DoSomething(item);
또는 목록 포함:
List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);
(앞에서 언급한 바와 같이)
MyMethodThatExpectsAnIEnumerable(new[] { myObject });
또는
MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));
참고로 마지막 버전은 익명 개체의 빈 목록을 원하는 경우에도 좋습니다.
var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));
저는 'As Singleton'이 더 나은 이름이라는 원래 게시물에 대한 @EarthEngine의 언급에 동의합니다.다음 위키백과 항목을 참조하십시오.그런 다음 null 값이 인수로 전달되면 'AsSingleton'이 다음을 해결하는 빈 IEnumber 대신 단일 null 값을 가진 IEnumber를 반환해야 한다는 것이 싱글턴의 정의에서 따온 것입니다.if (item == null) yield break;토론. 은 ' Singleton'As Empty' 두 가지입니다. 되면 ' SingletonOr Empty'는 단일 'As Empty는 빈 합니다.가장 좋은 해결책은 'As Singleton'과 'As SingletonOr Empty' 두 가지 방법을 사용하는 것입니다. 여기서 null이 인수로 전달되면 'As Singleton'은 단일 null 값을 반환하고 'As SingletonOr Empty'는 빈 IE 숫자를 반환합니다.다음과 같이:
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;
}
그런 다음, 이것들은 어느 정도 IEnumberable의 'First' 및 'FirstOrDefault' 확장 방법과 유사할 것입니다.
이는 보다 30% 더 빠릅니다.yield또는Enumerable.Repeat에 사용할 때.foreach이러한 C# 컴파일러 최적화로 인해, 그리고 다른 경우에도 동일한 성능을 발휘합니다.
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);
}
}
이 테스트:
// 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}");
}
LukeH 사용자가 제안한 바와 같이, 사용자 LukeH는 다음과 같은 간단한 방법을 제안했습니다.
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
}
}
이 패턴을 사용하면 단일 항목, 여러 항목(쉼표로 구분), 배열, 목록, 열거 등 다양한 방식으로 동일한 기능을 호출할 수 있습니다.
하지만 AsEnumerable 방법의 효율성에 대해 100% 확신할 수는 없지만 효과가 있습니다.
업데이트 : AsEnumberable 기능이 상당히 효율적으로 보입니다! (참고 자료)
한 가지 방법으로는 과잉 살상이지만, 어떤 사람들은 대화형 확장이 유용하다고 생각할 수도 있다고 생각합니다.
Microsoft의 Interactive Extensions(IX)에는 다음 방법이 포함되어 있습니다.
public static IEnumerable<TResult> Return<TResult>(TResult value)
{
yield return value;
}
다음과 같이 활용할 수 있습니다.
var result = EnumerableEx.Return(0);
Ix는 원래 Linq 확장 방법에는 없는 새로운 기능을 추가하며 Rx(Ractive Extensions)를 생성한 직접적인 결과입니다.
생해각,Linq Extension Methods+Ix=Rx위해서IEnumerable.
CodePlex에서 Rx와 Ix를 모두 찾을 수 있습니다.
나는 최근에 다른 게시물에서 같은 것을 물었습니다.
IEnumber가 필요한 C# 메서드를 호출할 수 있는 방법이 있습니까?<하나의 가치로? ...벤치마킹으로.
저는 사람들이 이 답변에 제시된 4가지 접근 방식에 대해 새로운 게시물에서 보여준 간략한 벤치마크 비교를 보기를 원했습니다.
단순히 글을 쓰는 것이new[] { x }방법에 대한 주장에서 가장 짧고 빠른 해결책입니다.
이것은 더 이상 좋지 않을 수도 있지만, 약간은 멋집니다.
Enumerable.Range(0, 1).Select(i => item);
@mattica가 위에서 언급한 유사한 질문에서 일부 벤치마킹 정보를 제공했지만, My benchmark tests는 다른 결과를 제공했습니다.
7에서 .NET yield return value 9% 름빠보다 ~new T[] { value }메모리 용량의 75%를 할당합니다.대부분의 경우 이는 이미 고성능이며 필요한 만큼의 성능을 제공합니다.
맞춤형 단일 수집 구현이 더 빠를지 아니면 더 가벼울지 궁금했습니다. 보니 알고보니보니▁it▁turns.yield return는 다과같구니다로 됩니다.IEnumerator<T>그리고.IEnumerable<T>할당 측면에서 그것을 능가하는 유일한 방법은 내 구현에서도 그렇게 하는 것입니다.
당신이 합격한다면요.IEnumerable<>외부 도서관에서는 당신이 만들고 있는 것에 대해 잘 알지 못하는 경우에는 이 작업을 하지 않는 것이 좋습니다.즉, 매우 단순한(재사용 안전하지 않은) 구현을 수행하여 수율 방법을 5ns만큼 능가하고 어레이의 절반만 할당했습니다.
왜냐하면 모든 시험이 통과되었기 때문입니다.IEnumerable<T>값 유형이 기준 유형보다 일반적으로 더 잘못 수행됩니다.제가 수행한 최고의 구현은 사실 가장 단순했습니다. 여러분은 다음과 같이 볼 수 있습니다.SingleCollection내가 링크한 요지의 수업. (이것은 2ns 더 빠름.yield return를 할당한 용량의 했습니다.yield return.)
TL:DR. 속도에 관심이 있다면 다음을 사용합니다.yield return item속도가 정말 중요한 경우 a를 사용합니다.
가끔 기분이 안 좋을 때 이렇게 해요.
"_".Select(_ => 3.14) // or whatever; any type is fine
이것은 키 누르기 횟수가 적을 때도 마찬가지입니다. 헤헤:
from _ in "_" select 3.14
유틸리티 함수의 경우, 여러 값을 생략할 수 있지만 배열보다 최소 장황하거나 최소한 자가 문서화가 더 많습니다. 추가적으로 로컬 함수로 정의할 수 있습니다.
static IEnumerable<T> Enumerate (params T[] v) => v;
// usage:
IEnumerable<double> example = Enumerate(1.234);
다음은 제가 생각할 수 있었던 다른 모든 방법입니다(여기서 실행 가능).
using System;
using System.Collections.Generic;
using System.Linq;
public class Program {
public static IEnumerable<T> ToEnumerable1 <T> (T v) {
yield return v;
}
public static T[] ToEnumerable2 <T> (params T[] vs) => vs;
public static void Main () {
static IEnumerable<T> ToEnumerable3 <T> (params T[] v) => v;
p( new string[] { "three" } );
p( new List<string> { "three" } );
p( ToEnumerable1("three") ); // our utility function (yield return)
p( ToEnumerable2("three") ); // our utility function (params)
p( ToEnumerable3("three") ); // our local utility function (params)
p( Enumerable.Empty<string>().Append("three") );
p( Enumerable.Empty<string>().DefaultIfEmpty("three") );
p( Enumerable.Empty<string>().Prepend("three") );
p( Enumerable.Range(3, 1) ); // only for int
p( Enumerable.Range(0, 1).Select(_ => "three") );
p( Enumerable.Repeat("three", 1) );
p( "_".Select(_ => "three") ); // doesn't have to be "_"; just any one character
p( "_".Select(_ => 3.3333) );
p( from _ in "_" select 3.0f );
p( "a" ); // only for char
// these weren't available for me to test (might not even be valid):
// new Microsoft.Extensions.Primitives.StringValues("three")
}
static void p <T> (IEnumerable<T> e) =>
Console.WriteLine(string.Join(' ', e.Select((v, k) => $"[{k}]={v,-8}:{v.GetType()}").DefaultIfEmpty("<empty>")));
}
제가 말하는 가장 쉬운 방법은new T[]{item};구문이 없습니다.할 수 은 내가생수있가가것동은한등운입니다.params물론 방법 정의에 액세스할 수 있어야 하며 어레이에서만 사용할 수 있습니다.
Enumerable.Range(1,1).Select(_ => {
//Do some stuff... side effects...
return item;
});
위의 코드는 like를 사용할 때 유용합니다.
var existingOrNewObject = MyData.Where(myCondition)
.Concat(Enumerable.Range(1,1).Select(_ => {
//Create my object...
return item;
})).Take(1).First();
위의 코드 스니펫에는 빈/null 검사가 없으며 예외를 두려워하지 않고 하나의 개체만 반환됩니다.게다가, 그것은 게으르기 때문에, 기준에 맞는 기존의 데이터가 없다는 것이 증명될 때까지 폐쇄는 실행되지 않을 것입니다.
"좋은 솔루션은 아니지만 그래도... 솔루션" 또는 "멍청한 LINQ 트릭"에 따라 다음과 결합할 수 있습니다.
IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Append("Hello, World!");
...또는...
IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Prepend("Hello, World!");
후자의 두 가지 방법은 .NET Framework 4.7.1 및 .NET Core 1.0 이후에 사용할 수 있습니다.
만약 누군가가 자신의 방법을 쓰는 대신 기존의 방법을 사용하는 것에 정말로 관심이 있다면, 이것은 실행 가능한 해결책입니다. 하지만 이것이 해결책보다 더 명확한지 덜 명확한지는 저는 결정하지 못했습니다.이것은 확실히 더 긴 코드입니다(부분적으로는 형식 매개변수 추론이 불가능하기 때문입니다).Empty<>()그러나 두 배의 열거자 개체를 만듭니다.
이 "이 방법이 존재한다는 것을 알고 있었습니까?"라는 답변을 정리하면, 대신할 수 있습니다.Enumerable.Empty<>()하지만 그렇다고 해서 상황이... 나은 더 나은.
저는 파티에 조금 늦었지만 어쨌든 제 길을 공유하겠습니다.문제는 ItemSource 또는 WPF TreeView를 단일 개체로 바인딩하려는 것이었습니다.계층 구조는 다음과 같습니다.
프로젝트 > 플롯 > 룸
항상 하나의 프로젝트만 있을 예정이었지만 일부 제안처럼 하나의 개체만 포함된 컬렉션을 통과할 필요 없이 트리에 프로젝트를 표시하고 싶었습니다.
ItemSource로 IEnumberable 객체만 전달할 수 있기 때문에 Itemberable 클래스를 다음과 같이 만들기로 결정했습니다.
public class ProjectClass : IEnumerable<ProjectClass>
{
private readonly SingleItemEnumerator<AufmassProjekt> enumerator;
...
public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator;
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
그리고 그에 따라 나만의 열거자를 만듭니다.
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;
}
이것은 아마도 "가장 깨끗한" 해결책은 아닐 것이지만 저에게는 효과가 있었습니다.
@Groo가 지적했듯이 단일 책임 원칙을 유지하기 위해 새 래퍼 클래스를 만들었습니다.
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();
}
내가 이렇게 사용했던 것.
TreeView.ItemSource = new SingleItemWrapper(itemToWrap);
2집편 2
저는 실수를 수정했습니다.MoveNext()방법.
선호합니다
public static IEnumerable<T> Collect<T>(this T item, params T[] otherItems)
{
yield return item;
foreach (var otherItem in otherItems)
{
yield return otherItem;
}
}
전화할 수 있습니다.item.Collect()싱글톤을 원한다면, 하지만 그것은 또한 당신이 전화할 수 있게 해줍니다.item.Collect(item2, item3)
언급URL : https://stackoverflow.com/questions/1577822/passing-a-single-item-as-ienumerablet
'code' 카테고리의 다른 글
| MVC4 번들의 {version} 와일드카드 (0) | 2023.06.02 |
|---|---|
| 문자열에서 특수 문자를 제거하는 가장 효율적인 방법 (0) | 2023.06.02 |
| RVM이 있는 Lion 아래에 Ruby를 설치할 수 없음 – GCC 문제 (0) | 2023.06.02 |
| j여러 요소에 대해 동일한 클릭 이벤트 쿼리 (0) | 2023.06.02 |
| Azure Application Insights - 타임스탬프의 일부로 요약 (0) | 2023.06.02 |