Krótsza składnia przerzucania z listy do listy?

Wiem, że możliwe jest rzucenie listy elementów z jednego typu do drugiego (biorąc pod uwagę, że Twój obiekt ma publiczną statyczną metodę jawnego operatora do wykonania odlewu) jeden po drugim w następujący sposób:

List<Y> ListOfY = new List<Y>();

foreach(X x in ListOfX)
    ListOfY.Add((Y)x);

Ale czy nie jest możliwe, aby rzucić całą listę w jednym czasie? Na przykład,

ListOfY = (List<Y>)ListOfX;
Author: StuartLC, 2011-02-25

4 answers

Jeśli {[1] } naprawdę można oddać do Y powinieneś być w stanie użyć

List<Y> listOfY = listOfX.Cast<Y>().ToList();

Niektóre rzeczy, aby być świadomym (H / t do komentatorów!)

 389
Author: Jamiec,
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-09-05 16:35:58

Bezpośredni rzut var ListOfY = (List<Y>)ListOfX nie jest możliwy, ponieważ wymagałby Ko / kontrawariancji typu List<T>, a to nie może być zagwarantowane w każdym przypadku. Czytaj dalej, aby zobaczyć rozwiązania tego problemu odlewania.

Chociaż wydaje się normalne, że można pisać kod w ten sposób:

List<Animal> animals = (List<Animal>) mammalList;
Ponieważ możemy zagwarantować, że każdy ssak będzie zwierzęciem, jest to oczywiście błąd:]}
List<Mammal> mammals = (List<Mammal>) animalList;
Ponieważ nie każde zwierzę jest ssakiem.

Jednakże, używając C# 3 i powyżej, możesz użyć

IEnumerable<Animal> animals = mammalList.Cast<Animal>();
To trochę ułatwia casting. Jest to syntaktycznie równoważne z Twoim kodem dodawania jeden po drugim, ponieważ używa jawnego obsady do obsady każdego Mammal na liście do Animal, i nie powiedzie się, jeśli obsada nie powiedzie się.

Jeśli chcesz mieć większą kontrolę nad procesem odlewania / konwersji, możesz użyć metody ConvertAll klasy List<T>, która może użyć dostarczonego wyrażenia do konwersji elementów. Posiada dodany benifit, który zwraca List, zamiast IEnumerable, więc nie jest konieczne .ToList().

List<object> o = new List<object>();
o.Add("one");
o.Add("two");
o.Add(3);

IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item
List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds
 84
Author: SWeko,
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-09-18 14:41:08

Aby dodać do punktu Sweko:

Powód, dla którego Obsada

var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y

Nie jest możliwe, ponieważ List<T> jest niezmiennikiem w typie T i dlatego nie ma znaczenia, czy X pochodzi z Y) - to dlatego, że List<T> jest zdefiniowane jako:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces

(zauważ, że w tej deklaracji Typ T tutaj nie ma dodatkowych modyfikatorów wariancji)

Jednakże, jeśli Kolekcje mutowalne nie są wymagane w Twoim projekcie, upcast na wielu z zbiory niezmienne, jest możliwe , np. pod warunkiem, że Giraffe wywodzi się z Animal:

IEnumerable<Animal> animals = giraffes;

Dzieje się tak dlatego, że IEnumerable<T> wspiera kowariancję w T - ma to sens biorąc pod uwagę, że IEnumerable implikuje, że kolekcja nie może zostać zmieniona, ponieważ nie ma wsparcia dla metod dodawania lub usuwania elementów z kolekcji. Zwróć uwagę na słowo kluczowe out w deklaracji IEnumerable<T>:

public interface IEnumerable<out T> : IEnumerable

(Oto dalsze wyjaśnienie powodu, dla którego zbiory mutowalne, takie jak List nie może obsługiwać covariance, podczas gdy niezmienne Iteratory i kolekcje mogą.)

Casting Z .Cast<T>()

Jak wspominali inni, .Cast<T>() może być zastosowany do zbioru, aby wyświetlić nowy zbiór elementów rzuconych do T, jednak w ten sposób rzuci InvalidCastException, jeśli odlew na jednym lub więcej elementach nie jest możliwy (co byłoby takim samym zachowaniem, jak wykonanie wyraźnego odlewu w pętli OP foreach).

Filtrowanie i odlewanie z OfType<T>()

Jeśli lista wejściowa zawiera elementy różnych, niezrozumiałych typów, potencjał InvalidCastException można uniknąć za pomocą .OfType<T>() zamiast .Cast<T>(). (.OfType<>() sprawdza, czy element może zostać przekonwertowany na typ docelowy, przed przystąpieniem do konwersji i odfiltrowuje typy niezrozumiałe.)

Foreach

Zauważ również, że gdyby OP napisał to zamiast: (zwróć uwagę na explicit Y y w foreach)

List<Y> ListOfY = new List<Y>();

foreach(Y y in ListOfX)
{
    ListOfY.Add(y);
}

Że casting będzie również próbowany. Jeśli jednak nie ma możliwości obsady, to wynik InvalidCastException.

Przykłady

Na przykład, biorąc pod uwagę prostą (C#6) hierarchię klas:
public abstract class Animal
{
    public string Name { get;  }
    protected Animal(string name) { Name = name; }
}

public class Elephant :  Animal
{
    public Elephant(string name) : base(name){}
}

public class Zebra : Animal
{
    public Zebra(string name)  : base(name) { }
}

Podczas pracy z kolekcją typów mieszanych:

var mixedAnimals = new Animal[]
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach(Animal animal in mixedAnimals)
{
     // Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
     castedAnimals.Add((Elephant)animal);
}

var castedAnimals = mixedAnimals.Cast<Elephant>()
    // Also fails for Zed with `InvalidCastException
    .ToList();

var castedAnimals = mixedAnimals.OfType<Elephant>()
    .ToList();
// Ellie

Odfiltrowuje tylko słonie - tzn. zebry są eliminowane.

Re: Implicit Cast operatory

Bez dynamicznego, zdefiniowanego przez użytkownika operatory konwersji są używane tylko w czasie kompilacji *, więc nawet jeśli operator konwersji między say Zebra i Elephant został udostępniony, powyższe zachowanie czasu wykonywania podejścia do konwersji nie uległoby zmianie.

Jeśli dodamy operator konwersji do konwersji zebry na słonia:

public class Zebra : Animal
{
    public Zebra(string name) : base(name) { }
    public static implicit operator Elephant(Zebra z)
    {
        return new Elephant(z.Name);
    }
}

Zamiast tego, biorąc pod uwagę powyższy operator konwersji, kompilator będzie mógł zmienić typ poniższej tablicy z Animal[] na Elephant[], biorąc pod uwagę, że zebry mogą być teraz przekształcony w jednorodny zbiór słoni:

var compilerInferredAnimals = new []
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

Korzystanie z niejawnych operatorów konwersji w czasie wykonywania

W 2008 roku Eric ogłosił, że będzie mógł korzystać z usług Operatora konwersji w czasie wykonywania operacji.]}
var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach (dynamic animal in mixedAnimals)
{
    castedAnimals.Add(animal);
}
// Returns Zed, Ellie
 8
Author: StuartLC,
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-05-22 14:56:37

Możesz użyć List<Y>.ConvertAll<T>([Converter from Y to T]);

 7
Author: Andrey,
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-05-25 09:51:00