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;
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!)
- musisz dołączyć
using System.Linq;
aby uzyskać tę metodę rozszerzenia - to rzuca każdy element na liście - nie samą listę. Nowy
List<Y>
zostanie utworzony przez wywołanieToList()
. - ta metoda nie obsługuje niestandardowych operatorów konwersji. ( patrz http://stackoverflow.com/questions/14523530/why-does-the-linq-cast-helper-not-work-with-the-implicit-cast-operator)
- ta metoda nie działa dla obiektu, który ma jawną metodę operatora (framework 4.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-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
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
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]);
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