2009-02-05 11 views
12

Angesichts der folgenden Klassendefinitionen:Kopieren einer Liste <BaseClass> zur Liste <DerivedClass>

public class BaseClass 
{ 
    public string SomeProp1 { get; set; } 
} 

public class DerivedClass : BaseClass 
{ 
    public string SomeProp2 { get; set; } 
} 

Wie kann ich eine List<BaseClass> nehmen und wandeln es in ein List<DerivedClass>?

In meinem realen Szenario BaseClass hat eine ganze Reihe von Eigenschaften, die ich nicht eins nach dem anderen kopieren muss (und dann behalten, wenn eine zusätzliche Eigenschaft hinzugefügt wird).

Das Hinzufügen eines parametrisierten Konstruktors zu BaseClass ist keine Option, da diese Klasse von einem WCF-Dienstverweis definiert wird.

+0

Generiert WCF nicht _partial_ classes in Proxy-Code? –

+0

Auf einen anderen Blick haben Sie nicht gesagt, ob DerivedClass nur eine funktionale Erweiterung über der Basis ist oder tatsächlich eine andere Datenstruktur haben muss, wie es das Codebeispiel vorschlägt. IMHO, im zweiten Fall, "Umwandlung" macht keinen Sinn. –

Antwort

13
List<DerivedClass> result = 
    listBaseClass.ConvertAll(instance => (DerivedClass)instance); 

Eigentlich ist ConvertAll gut, wenn Sie neue Objekte erstellen müssen auf der Grundlage der ursprünglichen, wenn Sie brauchen nur zu werfen können Sie die folgende

List<DerivedClass> result = 
    listBaseClass.Cast<DerivedClass>().ToList(); 

Wenn nicht alle Elemente in Ihrer Liste verwenden kann DerivedClass gegossen werden dann OfType stattdessen

List<DerivedClass> result = 
    listBaseClass.OfType<DerivedClass>().ToList(); 
+0

Ich werde es bereuen, Sie wieder hierher zu bringen, oder? ;) –

+0

Ich habe das von deinem Buch gelernt :-) –

+0

Das scheint nicht zu funktionieren ... –

15

Sie können das eigentliche Objekt nicht konvertieren, aber es ist einfach, eine neue Liste mit den konvertierten Inhalt zu erstellen:

List<BaseClass> baseList = new List<BaseClass>(...); 
// Fill it here... 

List<DerivedClass> derivedList = baseList.ConvertAll(b => (DerivedClass) b); 

Oder wenn Sie nicht C# 3 mit:

List<DerivedClass> derivedList = baseList.ConvertAll<DerivedClass>(delegate 
    (BaseClass b) { return (DerivedClass) b; }; 

Dies setzt voraus, dass die ursprüngliche Liste tatsächlich voll von Instanzen von DerivedClass war. Wenn dies nicht der Fall ist, ändern Sie den Delegaten, um basierend auf der angegebenen Basisklasse eine entsprechende Instanz von DerivedClass zu erstellen.

EDIT: Ich bin nicht sicher, warum ich nicht nur eine LINQ-Lösung schreiben:

List<DerivedClass> derivedList = baseList.Cast<DerivedClass>().ToList(); 
+0

Dies scheint nicht zu funktionieren. Ich denke, das liegt daran, dass Sie nicht von einer Basisklasse in eine abgeleitete Klasse umwandeln können. –

+0

@DanBailiff: Wie in der Antwort angegeben: "Dies setzt voraus, dass die ursprüngliche Liste tatsächlich mit Instanzen von DerivedClass gefüllt war." In diesem Fall funktioniert es *. Wenn Sie glauben, dass dies nicht der Fall ist, müssen Sie weitere Einzelheiten angeben. –

+0

@Jon Ich denke, du bist der unbestrittene König von C# –

3

(wiederholt von here)

Erste - beachten Sie, dass Sie können Konstrukteuren hinzufügen (und anderer Code) zu WCF-Klassen - Sie müssen es nur in einer partiellen Klasse tun (und den generierten Code in Ruhe lassen).

Es klingt wie die Typ der Elemente in der Liste müssen geändert werden - so können wir nicht nur zu werfen. Reflexion ist eine Option, ist aber langsam. Da Sie 3.5 verwenden, können wir vielleicht ein Expression zu tun es für uns effiziente schreiben ... entlang these lines, aber auch die zweite Klasse mit:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
static class Program 
{ 
    class Foo 
    { 
     public int Value { get; set; } 
     public override string ToString() 
     { 
      return Value.ToString(); 
     } 
    } 
    class Bar : Foo {} 
    static void Main() 
    { 
     List<Foo> foos = new List<Foo>(); 
     for (int i = 0; i < 10; i++) foos.Add(new Foo { Value = i }); 

     List<Bar> bars = foos.ConvertAll<Bar>(Clone<Foo, Bar>); 
    } 
    public static TTo Clone<TFrom, TTo>(this TFrom obj) where TTo : TFrom, new() 
    { 
     return ObjectExtCache<TFrom, TTo>.Convert(obj); 
    } 
    static class ObjectExtCache<TFrom, TTo> where TTo : TFrom, new() 
    { 
     private static readonly Func<TFrom, TTo> converter; 
     static ObjectExtCache() 
     { 
      ParameterExpression param = Expression.Parameter(typeof(TFrom), "in"); 
      var bindings = from prop in typeof(TFrom).GetProperties() 
          where prop.CanRead && prop.CanWrite 
          select (MemberBinding)Expression.Bind(prop, 
           Expression.Property(param, prop)); 
      converter = Expression.Lambda<Func<TFrom, TTo>>(
       Expression.MemberInit(
        Expression.New(typeof(TTo)), bindings), param).Compile(); 
     } 
     public static TTo Convert(TFrom obj) 
     { 
      return converter(obj); 
     } 
    } 
} 
2
using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Linq; 

namespace ConsoleApplication22 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<BaseClass> source = new List<BaseClass>(); 
      source.Add(new DerivedClass { Name = "One" }); 
      source.Add(new BaseClass()); 
      source.Add(new DerivedClass { Name = "Three" }); 

      List<DerivedClass> result = 
       new List<DerivedClass>(source.OfType<DerivedClass>()); 
      result.ForEach(i => Console.WriteLine(i.Name)); 
      Console.ReadLine(); 
     } 
    } 

    public class BaseClass 
    { 
    } 

    public class DerivedClass : BaseClass 
    { 
     public string Name { get; set; } 
    } 

} 

Dieser Code wird nicht nur konvertieren, aber enthalten auch nur Instanzen, die die abgeleitete Klasse sind, und alle, die dies nicht sind.

2

Wie andere vorgeschlagen haben, wenn Sie eine Instanz eines List<BaseClass> haben Sie es zu einem List<DerivedClass> mit der ConvertAll Methode von List umwandeln kann.

Allgemeiner gesagt, wenn Sie etwas haben, das IEnumerable<T> implementiert (die keine ConvertAll Methode hat) Sie Linq die verwenden können Cast das Gleiche zu tun:

IEnumerable<DerivedClass> result = listBaseClass.Cast<DerivedClass>(); 

Wenn Sie eine eine List zurück statt benötigen von einem IEnumerable, können Sie nur einen Anruf an ToList() am Ende.

Wie Jon schon sagte, wird davon ausgegangen, dass alle Einträge in listBaseClass tatsächlich vom Typ DerivedClass sind.

Verwandte Themen