wake-up-neo.com

C # Umwandlung einer Liste <ObjBase> als Liste <Obj>

Warum kann ich einen List<ObjBase> nicht als List<Obj> umwandeln? Warum funktioniert folgendes nicht:

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

}

Stattdessen muss ich Folgendes tun:

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

Ich erhalte die folgende Fehlermeldung in Visual Studio 2008 (aus Gründen der Lesbarkeit gekürzt):

Der Typ 'List' kann nicht über eine Referenz-, Boxing-, Unboxing-, Wrapping- oder Null-Typ-Konvertierung in 'List' konvertiert werden

Vielen Dank.

26
AndrewJacksonZA
9
gjutras

Sie können die Erweiterungsmethoden Cast und ToList von System.Linq verwenden, um dies in einer Zeile anzuzeigen.

Anstatt 

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

mach das:

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

Ich kann das "Problem" nur aus einer Java-Sicht beschreiben, aber von wem ich weiß, dass dieser Aspekt in C # und Java der gleiche ist:

Ein List<ObjBase> ist kein List<Obj>, da er ein ObjBase-Objekt enthalten kann, das kein Obj-Objekt ist.

Umgekehrt kann ein List<Obj>not nicht in einen List<ObjBase> umgewandelt werden, da der erstere garantiert, einen Add()-Aufruf mit einem ObjBase-Argument zu akzeptieren, das er nicht akzeptiert!

Um es zusammenzufassen: Auch wenn eine Obj-a ObjBase ein List<Obj>nicht eine List<ObjBase> ist.

10
Joachim Sauer
3
Dewfy

Ich denke, Sie verstehen die Besetzung, die Sie versuchen, falsch zu verstehen. Sie denken, Sie ändern den Typ des Objekts, das in der Liste gespeichert ist, und versuchen tatsächlich, den Typ der Liste selbst zu ändern. Es ist eher sinnvoll, dass Sie die Liste nicht ändern können, da Sie sie bereits ausgefüllt haben.

Sie können es als eine Liste einer Basisklasse betrachten und dann umwandeln, wenn Sie die Listenelemente verarbeiten. Das wäre mein Ansatz.

Was ist der Zweck dieser versuchten Besetzung?

1
Lazarus

Derzeit unterstützt C # keine Varianz für generische Typen. Nach dem, was ich gelesen habe, wird sich dies in 4.0 ändern.

Unter hier finden Sie weitere Informationen zu Abweichungen in Generika.

1
Scott Ivey

list.ConvertAll wirkt verführerisch, hat aber einen großen Nachteil: Es wird eine ganze neue Liste erstellt. Dies wirkt sich insbesondere bei großen Listen auf die Leistung und den Speicherverbrauch aus.

Mit etwas mehr Aufwand können Sie eine Wrapperlistenklasse erstellen, die die ursprüngliche Liste als interne Referenz enthält, und die Elemente nur konvertieren, wenn sie verwendet werden.

Verwendungszweck:

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

Code, der Ihrer Bibliothek hinzugefügt werden soll:

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

Linq verfügt über eine ConvertAll-Methode. so etwas wie 

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

Ich weiß nicht, was ich sonst vorschlagen soll. Ich gehe davon aus, dass ObjBase die Basisklasse ist, und wenn alle ObjBase-Objekte Obj-Objekte sind, bin ich mir nicht sicher, warum Sie die beiden Objekte an erster Stelle haben. Vielleicht bin ich falsch.

Bearbeiten: Die list.Cast-Methode würde besser funktionieren als die oben genannte, vorausgesetzt, sie sind untereinander gießbar. Ich habe das vergessen, bis ich die anderen Antworten gelesen habe.

0
Jimmeh

Lazarus :
Ich dachte, der Compiler würde erkennen, dass ich Aktionen für die Objekte der Liste ausführen wollte und nicht, dass ich versuchte, die Liste selbst zu erstellen.

Weitere Informationen:

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

Clientcode außerhalb dieses DLL kann jetzt ausgeführt werden: ObjManager objM = new ObjManager (); List listOB = objM.returnStuff (); Ich werde mehrere Obj- und ObjManager-Typen für diesen Teil (O/RM) der Anwendung erstellen.

(Der Darn-Kommentarblock hat keine Zeichen mehr!)

0
AndrewJacksonZA

Dies ist ein großer Schmerz in C # - so wurden Generika entwickelt. List erweitert List nicht, es ist nur ein völlig anderer Typ. Sie können sie auf keine Weise umsetzen oder zuweisen, Ihre einzige Möglichkeit ist, eine Liste in die andere zu kopieren.

0
Grzenio

So habe ich die Konvertierung von a behoben 

list<SomeOtherObject>

zu einem

object

und dann zu einem

List<object>

https://stackoverflow.com/a/16147909/2307326

0
Andrew Marais