Dowolnie konwertuj między List <T> i IEnumerable <T>

103

Jak mogę zamienić a List<MyObject>na an, IEnumerable<MyObject>a następnie ponownie?

Chcę to zrobić, aby uruchomić serię instrukcji LINQ na liście, np Sort()

TK.
źródło
To jest omówione w tym pytaniu stackoverflow.com/questions/31708/…
John

Odpowiedzi:

148
List<string> myList = new List<string>();
IEnumerable<string> myEnumerable = myList;
List<string> listAgain = myEnumerable.ToList();
Tamas Czinege
źródło
30
Uważaj jednak, że myEnumarable jest tą samą instancją obiektu co myList, ale listAgain nie jest tą samą instancją obiektu co myEnumerable. W zależności od tego, co chcesz zrobić i czym jest myEnumerable, "List <string> listAgain = myEnumerable as List <string>;" może być lepiej.
ChrisW,
1
Chrisw: O tak, masz oczywiście rację, ale interfejs IEnumerable <T> jest niezmienny, więc nie spowoduje problemów w tym kierunku, a odrzucanie z powrotem jest po prostu brudne, gdy masz funkcję, która zadba o bezpieczeństwo typów.
Tamas Czinege
1
Możesz także użyć oryginalnego obiektu myList. (Chyba nie rozumiem tego pytania)
sth
6
Po prostu, jeśli edytuje myList, będzie edytować myEnumrable, ale jeśli edytuje listAgain, nie będzie edytować myEnumerable.
ChrisW,
15
Nie zapomnij, using System.Linq;albo nie będziesz w stanie ToList ()
Jason
21

A List<T>jest IEnumerable<T>, więc w rzeczywistości nie ma potrzeby „konwertowania” a List<T>na IEnumerable<T>. Ponieważ a List<T>jest an IEnumerable<T>, możesz po prostu przypisać a List<T>do zmiennej typu IEnumerable<T>.

Z drugiej strony nie każdy IEnumerable<T>jest List<T>offcourse, więc będziesz musiał wywołać ToList()metodę składową IEnumerable<T>.

Frederik Gheysels
źródło
4
Technicznie rzecz biorąc, ToList jest metodą składową System.Linq.Enumerable
Amy B
2
Myślę, że możesz po prostu rzucić IEnumerable na Listę, tak jak powiedział David.
abatishchev
9

A List<T>już jest IEnumerable<T>, więc możesz uruchamiać instrukcje LINQ bezpośrednio na List<T>zmiennej.

Jeśli nie widzisz metod rozszerzenia LINQ, jak OrderBy()zgaduję, to dlatego, że nie masz using System.Linqdyrektywy w pliku źródłowym.

Musisz jednak przekonwertować wynik wyrażenia LINQ z powrotem na List<T>jawnie, chociaż:

List<Customer> list = ...
list = list.OrderBy(customer => customer.Name).ToList()
Dan Berindei
źródło
„Jeśli nie widzisz metod rozszerzenia LINQ, takich jak OrderBy ()” To był mój problem, dziękuję.
RyPope
5

Poza tym: Zwróć uwagę, że standardowe operatory LINQ (zgodnie z wcześniejszym przykładem) nie zmieniają istniejącej listy - list.OrderBy(...).ToList()utworzą nową listę na podstawie ponownie uporządkowanej sekwencji. Tworzenie metody rozszerzającej, która pozwala na używanie lambd z List<T>.Sort:

static void Sort<TSource, TValue>(this List<TSource> list,
    Func<TSource, TValue> selector)
{
    var comparer = Comparer<TValue>.Default;
    list.Sort((x,y) => comparer.Compare(selector(x), selector(y)));
}

static void SortDescending<TSource, TValue>(this List<TSource> list,
    Func<TSource, TValue> selector)
{
    var comparer = Comparer<TValue>.Default;
    list.Sort((x,y) => comparer.Compare(selector(y), selector(x)));
}

Następnie możesz użyć:

list.Sort(x=>x.SomeProp); // etc

Spowoduje to zaktualizowanie istniejącej listy w taki sam sposób, jak List<T>.Sortzwykle.

Marc Gravell
źródło
Niewielka poprawka do tego stwierdzenia: standardowe operatory LINQ nie zmieniają istniejącej listy; zamiast tego tworzą nowe IEnumerable, które zawierają logikę niezbędną do wykonywania ich pracy. Jednak ta logika nie jest faktycznie wykonywana, dopóki nie zostanie zażądany IEnumerator.
Vojislav Stojkovic
@Vojislav - miałem na myśli w kontekście wcześniejszego przykładu kończącego się na ToList- jednak wyjaśnię.
Marc Gravell
1

Konwersja List<T>doIEnumerable<T>

List<T>implements IEnumerable<T>(i wiele innych, takich jak IList<T>, ICollection<T>), dlatego nie ma potrzeby konwertowania listy z powrotem na IEnumerable, ponieważ jest to już plik IEnumerable<T>.

Przykład:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Person person1 = new Person() { Id = 1, Name = "Person 1" };
Person person2 = new Person() { Id = 2, Name = "Person 2" };
Person person3 = new Person() { Id = 3, Name = "Person 3" };

List<Person> people = new List<Person>() { person1, person2, person3 };

//Converting to an IEnumerable
IEnumerable<Person> IEnumerableList = people;

Możesz także użyć Enumerable.AsEnumerable()metody

IEnumerable<Person> iPersonList = people.AsEnumerable();

Konwersja IEnumerable<T>doList<T>

IEnumerable<Person> OriginallyIEnumerable = new List<Person>() { person1, person2 };
List<Person> convertToList = OriginallyIEnumerable.ToList();

Jest to przydatne w Entity Framework .

Nipuna
źródło
0

Aby zapobiec powielaniu w pamięci, Resharper sugeruje, że:

List<string> myList = new List<string>();
IEnumerable<string> myEnumerable = myList;
List<string> listAgain = myList as List<string>() ?? myEnumerable.ToList();

.ToList () zwraca nową niezmienną listę. Tak więc zmiany w listAgain nie mają wpływu na myList w odpowiedzi @Tamas Czinege. W większości przypadków jest to poprawne z co najmniej dwóch powodów: Pomaga to zapobiec zmianom w jednym obszarze wpływającym na inny obszar (luźne sprzężenie) i jest bardzo czytelne, ponieważ nie powinniśmy projektować kodu z problemami kompilatora.

Ale są pewne przypadki, takie jak bycie w ciasnej pętli lub praca w systemie osadzonym lub o małej ilości pamięci, w których należy wziąć pod uwagę kwestie związane z kompilatorem.

TamusJRoyce
źródło