Vượt qua một mục duy nhất dưới dạng IEn Countable <T>


377

Có một cách phổ biến để truyền một mục loại duy nhất Tcho một phương thức mong đợi một IEnumerable<T>tham số không? Ngôn ngữ là C #, phiên bản khung 2.0.

Hiện tại tôi đang sử dụng một phương thức của trình trợ giúp (đó là .Net 2.0, vì vậy tôi có cả đống phương thức trợ giúp truyền / chiếu tương tự như LINQ), nhưng điều này có vẻ ngớ ngẩn:

public static class IEnumerableExt
{
    // usage: IEnumerableExt.FromSingleItem(someObject);
    public static IEnumerable<T> FromSingleItem<T>(T item)
    {
        yield return item; 
    }
}

Cách khác tất nhiên sẽ là tạo và điền vào một List<T>hoặc một Arrayvà vượt qua nó thay vì IEnumerable<T>.

[Chỉnh sửa] Là một phương thức mở rộng, nó có thể được đặt tên:

public static class IEnumerableExt
{
    // usage: someObject.SingleItemAsEnumerable();
    public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
    {
        yield return item; 
    }
}

Am i thiếu cái gì ở đây?

[Edit2] Chúng tôi thấy someObject.Yield()(như @Peter đã đề xuất trong các bình luận bên dưới) là tên tốt nhất cho phương thức mở rộng này, chủ yếu là cho ngắn gọn, do đó, đây là cùng với nhận xét XML nếu có ai muốn lấy nó:

public static class IEnumerableExt
{
    /// <summary>
    /// Wraps this object instance into an IEnumerable&lt;T&gt;
    /// 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&lt;T&gt; consisting of a single item. </returns>
    public static IEnumerable<T> Yield<T>(this T item)
    {
        yield return item;
    }
}

6
Tôi sẽ thực hiện một sửa đổi nhỏ trong phần thân của phương thức mở rộng: if (item == null) yield break; Bây giờ bạn đã dừng chuyển null cũng như tận dụng mẫu đối tượng null (tầm thường) cho IEnumerable. ( foreach (var x in xs)xử lý một trống xschỉ tốt). Ngẫu nhiên, chức năng này là đơn vị đơn vị cho danh sách đơn vị IEnumerable<T>, và được tổ chức lễ hội tình yêu đơn nguyên tại Microsoft, tôi ngạc nhiên khi một thứ như thế này không có trong khuôn khổ ở nơi đầu tiên.
Matt Enright

2
Đối với phương thức tiện ích mở rộng, bạn không nên đặt tên cho nó AsEnumerablevì tiện ích mở rộng tích hợp với tên đó đã tồn tại . (Khi Tthực hiện IEnumerable, ví dụ , string.)
Jon-Eric

22
Làm thế nào về việc đặt tên phương pháp Yield? Không có gì nhịp đập.
Philip Guin

4
Gợi ý đặt tên ở đây. "SingleItemAsEnumerable" một chút dài dòng. "Yield" mô tả việc thực hiện thay vì giao diện - điều này không tốt. Để có tên tốt hơn, tôi đề xuất "AsSingleton", tương ứng với ý nghĩa chính xác của hành vi.
Động cơ Trái đất

4
Tôi ghét left==nullkiểm tra ở đây. Nó phá vỡ vẻ đẹp của mã và khiến mã trở nên linh hoạt hơn - điều gì sẽ xảy ra nếu một ngày nào đó bạn cần phải tạo ra một singleton với thứ gì đó có thể là null? Ý tôi là, new T[] { null }không giống như new T[] {}, và một ngày nào đó bạn có thể cần phân biệt chúng.
Động cơ Trái đất

Câu trả lời:


100

Phương pháp trợ giúp của bạn là cách sạch nhất để làm điều đó, IMO. Nếu bạn chuyển vào một danh sách hoặc một mảng, thì một đoạn mã vô đạo đức có thể bỏ nó và thay đổi nội dung, dẫn đến hành vi kỳ quặc trong một số tình huống. Bạn có thể sử dụng một bộ sưu tập chỉ đọc, nhưng điều đó có khả năng liên quan đến nhiều gói hơn. Tôi nghĩ rằng giải pháp của bạn là gọn gàng như nó được.


tốt nếu danh sách / mảng được xây dựng đặc biệt, phạm vi của nó kết thúc sau lệnh gọi phương thức, vì vậy nó không gây ra sự cố
Mario F

Nếu được gửi dưới dạng một mảng, làm thế nào nó có thể được thay đổi? Tôi đoán nó có thể được chuyển thành một mảng, và sau đó thay đổi tham chiếu sang một thứ khác, nhưng điều đó có ích gì? (Có lẽ tôi đang thiếu thứ gì đó ...)
Svish

2
Giả sử bạn đã quyết định tạo một vô số để chuyển sang hai phương thức khác nhau ... thì phương thức đầu tiên sẽ chuyển nó thành một mảng và thay đổi nội dung. Sau đó, bạn chuyển nó làm đối số cho một phương thức khác, không biết về sự thay đổi. Tôi không nói là có khả năng, chỉ là có một thứ gì đó có thể thay đổi khi nó không cần phải gọn gàng hơn bất biến naturla.
Jon Skeet

3
Đây là một câu trả lời được chấp nhận và rất có thể sẽ được đọc, vì vậy tôi sẽ thêm mối quan tâm của tôi ở đây. Tôi đã thử phương pháp này, nhưng nó đã phá vỡ cuộc gọi biên dịch trước đó của tôi đến myDict.Keys.AsEnumerable()nơi myDictthuộc loại Dictionary<CheckBox, Tuple<int, int>>. Sau khi tôi đổi tên phương thức mở rộng thành SingleItemAsEnumerable, mọi thứ bắt đầu hoạt động. Không thể chuyển đổi IEnumerable <Dictionary <CheckBox, System.Tuple <int, int >>. KeyCollection> 'thành' IEnumerable <CheckBox> '. Một chuyển đổi rõ ràng tồn tại (bạn có thiếu một diễn viên không?) Tôi có thể sống với một vụ hack trong một thời gian, nhưng các tin tặc quyền lực khác có thể tìm thấy một cách tốt hơn không?
Hamish Grubijan

3
@ HamishGrubijan: Có vẻ như bạn muốn dictionary.Valuesbắt đầu với. Về cơ bản không rõ chuyện gì đang xảy ra, nhưng tôi khuyên bạn nên bắt đầu một câu hỏi mới về nó.
Jon Skeet

133

Chà, nếu phương thức mong muốn IEnumerablebạn phải vượt qua một cái gì đó là một danh sách, ngay cả khi nó chỉ chứa một yếu tố.

đi qua

new[] { item }

như tôi tranh luận là đủ


32
Tôi nghĩ bạn thậm chí có thể bỏ T, vì nó sẽ được suy ra (trừ khi tôi rất sai ở đây: p)
Svish

2
Tôi nghĩ rằng nó không được suy luận trong C # 2.0.
Groo

4
"Ngôn ngữ là C #, khung phiên bản 2" Trình biên dịch C # 3 có thể suy ra T và mã sẽ tương thích hoàn toàn với .NET 2.
sisve

câu trả lời tốt nhất là ở phía dưới?!
Falco Alexander

2
@Svish Tôi đã đề xuất và chỉnh sửa câu trả lời để thực hiện điều đó - hiện tại chúng tôi đang ở năm 2020, vì vậy nó phải là "tiêu chuẩn"
imho

120

Trong C # 3.0, bạn có thể sử dụng lớp System.Linq.Enumerable:

// using System.Linq

Enumerable.Repeat(item, 1);

Điều này sẽ tạo ra một IEnumerable mới chỉ chứa mục của bạn.


26
Tôi nghĩ rằng giải pháp này sẽ làm cho mã khó đọc hơn. :( Lặp lại trên một yếu tố duy nhất khá phản cảm, bạn có nghĩ vậy không?
Drahakar

6
Lặp lại một lần đọc ok với tôi. Giải pháp đơn giản hơn nhiều mà không phải lo lắng về việc thêm một phương thức mở rộng khác và đảm bảo không gian tên được bao gồm bất cứ nơi nào bạn muốn sử dụng.
si618

13
Vâng tôi thực sự chỉ sử dụng new[]{ item }bản thân mình, tôi chỉ nghĩ rằng đó là một giải pháp thú vị.
luksan

7
Giải pháp này là tiền.
ProVega

IMHO đây sẽ là câu trả lời được chấp nhận. Thật dễ đọc, súc tích và được triển khai trong một dòng duy nhất trên BCL mà không cần phương thức mở rộng tùy chỉnh.
Ryan

35

Trong C # 3 (tôi biết bạn đã nói 2), bạn có thể viết một phương thức mở rộng chung có thể khiến cú pháp dễ chấp nhận hơn một chút:

static class IEnumerableExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this T item)
    {
        yield return item;
    }
}

mã khách hàng là sau đó item.ToEnumerable().


Cảm ơn, tôi biết điều đó (hoạt động trong .Net 2.0 nếu tôi sử dụng C # 3.0), tôi chỉ tự hỏi liệu có một cơ chế tích hợp nào cho việc này không.
Groo

12
Đây là một thay thế thực sự tốt đẹp. Tôi chỉ muốn đề xuất đổi tên để ToEnumerabletránh gây rối vớiSystem.Linq.Enumerable.AsEnumerable
Fede

3
Là một lưu ý phụ, đã tồn tại một ToEnumerablephương thức mở rộng cho IObservable<T>, vì vậy tên phương thức này cũng can thiệp vào các quy ước hiện có. Thành thật mà nói, tôi khuyên bạn không nên sử dụng một phương thức mở rộng, đơn giản vì nó quá chung chung.
cwharris

14

Phương pháp trợ giúp này hoạt động cho mục hoặc nhiều.

public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
    return items;
}    

1
Biến thể hữu ích, vì nó hỗ trợ nhiều hơn một mục. Tôi thích rằng nó dựa vào paramsđể xây dựng mảng, vì vậy mã kết quả trông rất rõ ràng. Không quyết định liệu tôi thích nó nhiều hay ít hơn new T[]{ item1, item2, item3 };cho nhiều mặt hàng.
ToolmakerSteve

Thông minh ! Yêu nó
Mitsuru Furuta

10

Tôi hơi ngạc nhiên khi không ai đề xuất một phương thức quá tải mới với đối số loại T để đơn giản hóa API khách.

public void DoSomething<T>(IEnumerable<T> list)
{
    // Do Something
}

public void DoSomething<T>(T item)
{
    DoSomething(new T[] { item });
}

Bây giờ mã khách hàng của bạn chỉ có thể làm điều này:

MyItem item = new MyItem();
Obj.DoSomething(item);

hoặc với một danh sách:

List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);

19
Thậm chí tốt hơn, bạn có thể có DoSomething<T>(params T[] items)nghĩa là trình biên dịch sẽ xử lý việc chuyển đổi từ một mục đơn sang một mảng. (Điều này cũng sẽ cho phép bạn chuyển qua nhiều mục riêng biệt và một lần nữa, trình biên dịch sẽ xử lý chuyển đổi chúng thành một mảng cho bạn.)
LukeH

7

Hoặc (như đã nói trước đây)

MyMethodThatExpectsAnIEnumerable(new[] { myObject });

hoặc là

MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));

Là một lưu ý phụ, phiên bản cuối cùng cũng có thể tốt nếu bạn muốn một danh sách trống của một đối tượng ẩn danh, ví dụ:

var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));

Cảm ơn, mặc dù Enumerable.Repeat là mới trong .Net 3.5. Nó dường như hành xử tương tự như phương pháp trợ giúp ở trên.
Groo

7

Như tôi vừa tìm thấy và thấy rằng người dùng LukeH cũng đề xuất, một cách đơn giản để thực hiện việc này như sau:

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
    }
}

Mẫu này sẽ cho phép bạn gọi cùng một chức năng theo nhiều cách: một mục duy nhất; nhiều mục (được phân tách bằng dấu phẩy); một mảng; một danh sách; một bảng liệt kê, vv

Tôi không chắc chắn 100% về hiệu quả của việc sử dụng phương pháp AsEnumerable, nhưng nó thực sự hiệu quả.

Cập nhật: Chức năng AsEnumerable trông khá hiệu quả! ( tham khảo )


Thật ra, bạn không cần .AsEnumerable()gì cả. Mảng YourType[]đã thực hiện IEnumerable<YourType>. Nhưng câu hỏi của tôi là đề cập đến trường hợp khi chỉ có phương thức thứ hai (trong ví dụ của bạn) có sẵn và bạn đang sử dụng .NET 2.0 và bạn muốn truyền một mục duy nhất.
Groo

2
Vâng, đúng vậy, nhưng bạn sẽ thấy bạn có thể bị tràn ngăn xếp ;-)
teppicymon

Và để thực sự trả lời câu hỏi thực sự của bạn (không phải câu hỏi mà tôi thực sự muốn tự trả lời!), Vâng, bạn phải chuyển đổi nó thành một mảng theo câu trả lời của Mario
teppicymon

2
Làm thế nào về việc chỉ định nghĩa một IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}phương thức? Sau đó, người ta có thể sử dụng nó với bất cứ thứ gì mong đợi IEnumerable<T>, cho bất kỳ số lượng các mục riêng biệt. Mã về cơ bản phải hiệu quả như xác định một lớp đặc biệt để trả về một mục duy nhất.
supercat

6

Mặc dù nó quá mức cho một phương pháp, tôi tin rằng một số người có thể thấy Tiện ích mở rộng tương tác hữu ích.

Phần mở rộng tương tác (Ix) từ Microsoft bao gồm phương pháp sau.

public static IEnumerable<TResult> Return<TResult>(TResult value)
{
    yield return value;
}

Mà có thể được sử dụng như vậy:

var result = EnumerableEx.Return(0);

Ix thêm chức năng mới không được tìm thấy trong các phương thức mở rộng Linq ban đầu và là kết quả trực tiếp của việc tạo Tiện ích mở rộng phản ứng (Rx).

Hãy suy nghĩ, Linq Extension Methods+ Ix= Rxcho IEnumerable.

Bạn có thể tìm thấy cả Rx và Ix trên CodePlex .


5

Tốc độ này nhanh hơn 30% so với yieldhoặc Enumerable.Repeatkhi được sử dụng foreachdo tối ưu hóa trình biên dịch C # này và có cùng hiệu suất trong các trường hợp khá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);
    }
}

trong bài kiểm tra này:

    // 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}");
    }

Cảm ơn, thật hợp lý khi tạo một cấu trúc cho trường hợp này để giảm gánh nặng GC trong các vòng lặp chặt chẽ.
Groo

1
Cả liệt kê và liệt kê sẽ được đóng hộp khi trở về ....
Stephen Drew

2
Đừng so sánh bằng cách sử dụng Đồng hồ bấm giờ. Sử dụng Benchmark.NET github.com/dotnet/BenchmarkDotNet
NN_

1
Chỉ cần đo nó và new [] { i }tùy chọn là nhanh hơn khoảng 3,2 lần sau đó tùy chọn của bạn. Ngoài ra tùy chọn của bạn là nhanh hơn khoảng 1,2 lần sau đó mở rộng với yield return.
lorond

Bạn có thể tăng tốc độ này bằng cách sử dụng tối ưu hóa trình biên dịch C # khác: thêm một phương thức SingleEnumerator GetEnumerator()trả về cấu trúc của bạn. Trình biên dịch C # sẽ sử dụng phương thức này (nó tìm kiếm thông qua gõ vịt) thay vì giao diện và do đó tránh được quyền anh. Nhiều bộ sưu tập tích hợp sử dụng thủ thuật này, như Danh sách . Lưu ý: điều này sẽ chỉ hoạt động khi bạn có một refercne trực tiếp SingleSequence, như trong ví dụ của bạn. Nếu bạn lưu trữ nó trong một IEnumerable<>biến thì thủ thuật sẽ không hoạt động (phương thức giao diện sẽ được sử dụng)
enzi

5

IanGmột bài viết hay về chủ đề này , gợi ý EnumerableFrom()như tên (và đề cập rằng Haskell và Rx gọi nó Return). IIRC F # gọi nó là Trả lại quá . F # 's Seqgọi tổng đàisingleton<'T> .

Hấp dẫn nếu bạn chuẩn bị trở thành C # -centric là gọi nó Yield[ám chỉ những người yield returnliên quan đến việc nhận ra nó].

Nếu bạn quan tâm đến các khía cạnh hoàn hảo của nó, James Michael Hare có số 0 hoặc một mục được gửi lại cũng rất đáng để quét.


4

Điều này có thể không tốt hơn nhưng nó khá tuyệt:

Enumerable.Range(0, 1).Select(i => item);

Nó không thể. Nó khác nhau.
nawfal

Ewww. Đó là rất nhiều chi tiết, chỉ để biến một mục thành vô số.
ToolmakerSteve

1
Điều này tương đương với việc sử dụng máy Rube Goldberg để làm bữa sáng. Vâng, nó hoạt động, nhưng nó nhảy qua 10 vòng để đến đó. Một điều đơn giản như thế này có thể là nút cổ chai hiệu năng khi được thực hiện trong một vòng lặp chặt chẽ được thực hiện hàng triệu lần. Trong thực tế, khía cạnh hiệu suất không thành vấn đề trong 99% trường hợp, nhưng cá nhân tôi vẫn nghĩ rằng nó quá mức cần thiết quá mức cần thiết.
enzi

4

Tôi đồng ý với ý kiến ​​của @ EarthEngine cho bài viết gốc, đó là 'AsSingleton' là một cái tên hay hơn. Xem mục wikipedia này . Sau đó, nó xuất phát từ định nghĩa của singleton rằng nếu một giá trị null được thông qua dưới dạng đối số 'AsSingleton' sẽ trả về một IEnumerable với một giá trị null thay vì một IEnumerable trống sẽ giải quyết if (item == null) yield break;cuộc tranh luận. Tôi nghĩ giải pháp tốt nhất là có hai phương pháp: 'AsSingleton' và 'AsSingletonOrEmpty'; trong đó, trong trường hợp null được truyền dưới dạng đối số, 'AsSingleton' sẽ trả về một giá trị null duy nhất và 'AsSingletonOrEmpty' sẽ trả về một IEnumerable trống. Như thế này:

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;
}

Sau đó, những cái này, ít nhiều sẽ tương tự như các phương thức mở rộng 'First' và 'FirstOrDefault' trên IEnumerable mà cảm thấy đúng.


2

Cách dễ nhất tôi muốn nói là new T[]{item};; không có cú pháp để làm điều này. Tương đương gần nhất mà tôi có thể nghĩ đến là paramstừ khóa, nhưng tất nhiên điều đó đòi hỏi bạn phải có quyền truy cập vào định nghĩa phương thức và chỉ có thể sử dụng được với các mảng.


2
Enumerable.Range(1,1).Select(_ => {
    //Do some stuff... side effects...
    return item;
});

Đoạn mã trên rất hữu ích khi sử dụng like

var existingOrNewObject = MyData.Where(myCondition)
       .Concat(Enumerable.Range(1,1).Select(_ => {
           //Create my object...
           return item;
       })).Take(1).First();

Trong đoạn mã trên không có kiểm tra trống / null và được đảm bảo chỉ có một đối tượng được trả về mà không sợ ngoại lệ. Hơn nữa, vì nó lười biếng, việc đóng sẽ không được thực thi cho đến khi được chứng minh là không có dữ liệu hiện có phù hợp với tiêu chí.


Câu trả lời này đã được tự động gắn thẻ "chất lượng thấp". Như đã giải thích trong trợ giúp ("Brevity có thể chấp nhận được, nhưng giải thích đầy đủ hơn sẽ tốt hơn."), Vui lòng chỉnh sửa nó để nói với OP những gì anh ta đang làm sai, mã của bạn là gì.
Sandra Rossi

Tôi thấy phần lớn câu trả lời này, bao gồm cả phần tôi đang trả lời, đã được người khác chỉnh sửa sau đó. nhưng nếu mục đích của đoạn mã thứ hai là cung cấp giá trị mặc định nếu MyData.Where(myCondition)trống, điều đó đã có thể (và đơn giản hơn) với DefaultIfEmpty(): var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();. Điều đó có thể được đơn giản hóa hơn nữa var existingOrNewObject = MyData.FirstOrDefault(myCondition);nếu bạn muốn default(T)và không phải là một giá trị tùy chỉnh.
BACON

0

Tôi đến bữa tiệc muộn một chút nhưng dù sao tôi cũng sẽ chia sẻ. Vấn đề của tôi là tôi muốn liên kết ItemSource hoặc WPF TreeView với một đối tượng. Hệ thống phân cấp trông như thế này:

Dự án> Lô đất> Phòng (s)

Luôn luôn chỉ có một Dự án nhưng tôi vẫn muốn Hiển thị dự án trong Cây, mà không phải thông qua Bộ sưu tập chỉ có một đối tượng trong đó như một số đề xuất.
Vì bạn chỉ có thể vượt qua các đối tượng IEnumerable dưới dạng ItemSource, tôi đã quyết định tạo lớp IEnumerable của mình:

public class ProjectClass : IEnumerable<ProjectClass>
{
    private readonly SingleItemEnumerator<AufmassProjekt> enumerator;

    ... 

    public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator;

    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}

Và tạo En Enatorator của riêng tôi theo đó:

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;
}

Đây có lẽ không phải là giải pháp "sạch nhất" nhưng nó hiệu quả với tôi.

EDIT
Để duy trì nguyên tắc trách nhiệm duy nhất như @Groo đã chỉ ra tôi đã tạo một lớp bao bọc mới:

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();
}

Mà tôi đã sử dụng như thế này

TreeView.ItemSource = new SingleItemWrapper(itemToWrap);

EDIT 2
Tôi đã sửa một lỗi với MoveNext()phương pháp.


Các SingleItemEnumerator<T>lớp học có ý nghĩa, nhưng thực hiện một lớp một "mục duy nhất IEnumerablecủa chính nó" có vẻ như một sự vi phạm nguyên tắc trách nhiệm duy nhất. Có lẽ nó làm cho nó đi qua thực tế hơn, nhưng tôi vẫn thích bọc nó khi cần thiết.
Groo

0

Để được nộp theo "Không nhất thiết là một giải pháp tốt , nhưng vẫn ... một giải pháp" hoặc "Thủ thuật LINQ ngu ngốc", bạn có thể kết hợp Enumerable.Empty<>()với Enumerable.Append<>()...

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Append("Hello, World!");

... hoặc Enumerable.Prepend<>()...

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Prepend("Hello, World!");

Hai phương thức sau có sẵn kể từ .NET Framework 4.7.1 và .NET Core 1.0.

Đây là một giải pháp khả thi nếu ai đó thực sự có ý định sử dụng phương pháp hiện có thay vì viết riêng của họ, mặc dù tôi chưa quyết định nếu điều này là nhiều hơn hoặc ít rõ ràng hơn các Enumerable.Repeat<>()giải pháp . Đây chắc chắn là mã dài hơn (một phần do suy luận tham số loại không thể thực hiện đượcEmpty<>() ) và tuy nhiên, tạo ra gấp đôi số đối tượng liệt kê.

Làm tròn điều này "Bạn có biết những phương pháp này tồn tại?" câu trả lời, Array.Empty<>()có thể được thay thế Enumerable.Empty<>(), nhưng thật khó để tranh luận rằng làm cho tình hình ... tốt hơn.


0

Gần đây tôi đã hỏi điều tương tự trên một bài đăng khác ( Có cách nào để gọi phương thức C # yêu cầu <T> IEn đếm được với một giá trị không? ... với điểm chuẩn ).

Tôi muốn mọi người dừng lại ở đây để xem so sánh điểm chuẩn ngắn gọn được hiển thị tại bài đăng mới hơn cho 4 trong số các cách tiếp cận được trình bày trong các câu trả lời này.

Có vẻ như chỉ cần viết new[] { x }trong các đối số cho phương thức là giải pháp ngắn nhất và nhanh nhất.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.