2009-08-12 11 views
24

Dlaczego nie mogę obsłużyć List<ObjBase> jako List<Obj>? Dlaczego nie następujące prace:C# Casting a List <ObjBase> jako List <Obj>

internal class ObjBase 
    { 
    } 

internal class Obj : ObjBase 
    { 
    } 

internal class ObjManager 
{ 
    internal List<Obj> returnStuff() 
    { 
     return getSomeStuff() as List<Obj>; 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 

} 

Zamiast muszę to zrobić:

internal class ObjBase 
    { 
    } 

internal class Obj : ObjBase 
    { 
    } 

internal class ObjManager 
{ 
    internal List<Obj> returnStuff() 
    { 
     List<ObjBase> returnedList = getSomeStuff(); 
     List<Obj> listToReturn = new List<Obj>(returnedList.Count); 
     foreach (ObjBase currentBaseObject in returnedList) 
     { 
      listToReturn.Add(currentBaseObject as Obj); 
     } 
     return listToReturn; 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 
} 

otrzymuję następujący błąd w Visual Studio 2008 (skrócony dla czytelności):

nie potrafisz Konwertuj typ "Lista" na "Lista" poprzez konwersję referencyjną, konwersję boksu, konwersję rozpakowywania, konwersję owijania lub konwersję typu null

Dzięki.

+0

-1: Dup wielu pytań tutaj, w tym http://stackoverflow.com/questions/1263489/how- do-i-upcast-a-collection-of-base-class-in-c/1263514 # 1263514 i te wymienione w odpowiedzi poniżej –

+0

Prawdopodobny duplikat [.NET Casting Generic List] (http://stackoverflow.com/questions/674715/net-casting-generic-list) –

Odpowiedz

1

Myślę, że nie rozumiesz obsady, którą próbujesz zrobić. Myślisz, że zmieniasz typ obiektu, który jest przechowywany na liście, gdzie faktycznie próbujesz zmienić typ samej listy. Ma raczej sens, że nie możesz zmienić samej listy, ponieważ już ją zapełniłeś.

Możesz na to spojrzeć jako na listę klasy bazowej, a następnie przesłać ją, gdy przetwarzasz elementy listy, to byłoby moje podejście.

Jaki jest cel tej próby obsady?

0

LINQ ma metodę convertall. więc coś takiego jak

list.ConvertAll<Obj>(objBase => objbase.ConvertTo(obj)); 

Nie jestem pewien, co jeszcze zasugerować. Zakładam, że ObjBase jest klasą podstawową, a jeśli wszystkie obiekty ObjBase są obiektami Obj, nie jestem pewien, dlaczego miałbyś te dwa obiekty na pierwszym miejscu. Być może jestem nie na miejscu.

Edycja: metoda list.Cast będzie działać lepiej niż powyższe, zakładając, że można je rzutować na siebie. Zapomniałem o tym, dopóki nie przeczytałem innych odpowiedzi.

9

mogę opisać tylko „problem” z widokiem Java, ale z tego co trochę znam ten aspekt jest taki sam zarówno w C# i Java:

List<ObjBase> nie jest List<Obj>, ponieważ może on zawierać obiekt, który nie jest obiektem Obj.

Innym sposobem wokół List<Obj> może nie być oddane do List<ObjBase> bo byłe gwarancje zaakceptować Add() połączenia z ObjBase argumentem, który ten ostatni nie przyjmie!

Więc Podsumowując: chociaż Obj is-a ObjBaseList<Obj> jest nieList<ObjBase>.

+0

To jest najprostszy opis ogólnego problemu kowariancyjnego, który przeczytałem. Miły. –

1

C# obecnie nie obsługuje wariancji dla typów ogólnych. Z tego co przeczytałem, zmieni się to w 4.0.

Zobacz here, aby uzyskać więcej informacji na temat wariancji w generycznych.

32

Można użyć metod rozszerzających Cast i ToList z System.Linq, aby uzyskać to w jednym wierszu.

Zamiast

internal List<Obj> returnStuff() 
{ 
    return getSomeStuff() as List<Obj>; 
} 

to zrobić:

internal List<Obj> returnStuff() 
{ 
    return getSomeStuff().Cast<Obj>().ToList(); 
} 
0

Jest to poważny ból w C# - jest to jak leki generyczne zostały zaprojektowane. Lista nie rozszerza listy, jest to po prostu zupełnie inny typ. Nie można ich w żaden sposób rzutować ani przypisywać, jedyną opcją jest skopiowanie jednej listy do drugiej.

0

Lazarus: Pomyślałem, że kompilator zda sobie sprawę, że chciałbym wykonać akcje na obiektach listy, a nie to, że próbowałem rzucić samą listę.

Niektórzy więcej informacji:

public abstract class ObjBase 
    { 
    } 

internal interface IDatabaseObject 
    { 
    } 

public class Obj : ObjBase, IDatabaseObject 
    { 
    } 


internal interface IDatabaseObjectManager 
    { 
     List<ObjBase> getSomeStuff(); 
    } 

public class ObjManager : IObjManager 
{ 
    public List<Obj> returnStuff() 
    { 
     return getSomeStuff().Cast <Customer>().ToList<Customer>(); 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 
} 

Teraz kod klienta poza tym DLL może pójść: ObjManager objM = new ObjManager(); Lista listOB = objM.returnStuff(); Mam zamiar utworzyć kilka typów Obj i ObjManager dla tej części (O/RM) aplikacji.

(Darn komentarz blok zabrakło bohaterów! :-)

1

list.ConvertAll wygląda kusząco, ale ma jedną wielką wadę: stworzy całkowicie nową listę. Wpłynie to na wydajność i wykorzystanie pamięci, szczególnie w przypadku dużych list.

Przy odrobinę większym wysiłku można utworzyć klasę list zawijanych, która zachowuje oryginalną listę jako wewnętrzne odniesienie i konwertować elementy tylko wtedy, gdy są używane.

Zastosowanie:

var x = new List<ObjBase>(); 
var y = x.CastList<ObjBase, Obj>(); // y is now an IList<Obj> 

kod, aby dodać do swojej biblioteki:

public static class Extensions 
{ 
    public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list) 
    { 
     return new CastedList<TTo, TFrom>(list); 
    } 
} 

public class CastedList<TTo, TFrom> : IList<TTo> 
{ 
    public IList<TFrom> BaseList; 

    public CastedList(IList<TFrom> baseList) 
    { 
     BaseList = baseList; 
    } 

    // IEnumerable 
    IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); } 

    // IEnumerable<> 
    public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); } 

    // ICollection 
    public int Count { get { return BaseList.Count; } } 
    public bool IsReadOnly { get { return BaseList.IsReadOnly; } } 
    public void Add(TTo item) { BaseList.Add((TFrom)(object)item); } 
    public void Clear() { BaseList.Clear(); } 
    public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); } 
    public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); } 
    public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); } 

    // IList 
    public TTo this[int index] 
    { 
     get { return (TTo)(object)BaseList[index]; } 
     set { BaseList[index] = (TFrom)(object)value; } 
    } 

    public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); } 
    public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); } 
    public void RemoveAt(int index) { BaseList.RemoveAt(index); } 
} 

public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo> 
{ 
    public IEnumerator<TFrom> BaseEnumerator; 

    public CastedEnumerator(IEnumerator<TFrom> baseEnumerator) 
    { 
     BaseEnumerator = baseEnumerator; 
    } 

    // IDisposable 
    public void Dispose() { BaseEnumerator.Dispose(); } 

    // IEnumerator 
    object IEnumerator.Current { get { return BaseEnumerator.Current; } } 
    public bool MoveNext() { return BaseEnumerator.MoveNext(); } 
    public void Reset() { BaseEnumerator.Reset(); } 

    // IEnumerator<> 
    public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } } 
}