2009-02-05 26 views
12

otrzymuje następujące definicje klasy:Kopiowanie Lista <BaseClass> do listy <DerivedClass>

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

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

Jak mogę wziąć List<BaseClass> i przekształcić go w List<DerivedClass>?

W moim prawdziwym scenariuszu BaseClass ma całą masę właściwości, których nie chcę kopiować pojedynczo (a następnie pamiętaj, aby zachować, jeśli zostanie dodana dodatkowa właściwość).

Dodawanie sparametryzowanego konstruktora do BaseClass nie jest opcją, ponieważ ta klasa jest zdefiniowana przez odwołanie do usługi WCF.

+0

Czy WCF nie generuje _partial_ classes w kodzie proxy? –

+0

Na drugi rzut oka nie powiedziałeś, że DerivedClass jest tylko rozszerzeniem funkcjonalnym na bazie lub faktycznie musi być inną strukturą danych, jak sugeruje to przykład kodu. IMHO, w drugim przypadku "konwersja" nie ma sensu. –

Odpowiedz

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

Właściwie convertall jest dobre, kiedy trzeba stworzyć nowe obiekty na podstawie oryginału, kiedy po prostu trzeba rzucić można użyć następujących

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

Jeśli nie wszystkie pozycje na liście mogą być oddane do DerivedClass następnie użyć OfType zamiast

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

Będę żałować, że cię tu skusiłem, prawda? ;) –

+0

Nauczyłem się tego z twojej książki :-) –

+0

To nie wydaje się działać ... –

15

nie można konwertować rzeczywisty obiekt, ale jest to łatwe, aby utworzyć nową listę z przekształconych treści:

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

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

Lub jeśli nie używasz C# 3:

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

ta zakłada, że ​​pierwotna lista była rzeczywiście pełne wystąpień DerivedClass. Jeśli tak nie jest, zmień delegata, aby utworzyć odpowiednie wystąpienie DerivedClass na podstawie podanej klasy BaseClass.

EDIT: Nie jestem pewien, dlaczego nie po prostu pisać rozwiązanie LINQ:

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

To nie działa. Myślę, że to dlatego, że nie można rzutować z klasy bazowej na klasę pochodną? –

+0

@DanBailiff: jak stwierdzono w odpowiedzi: "Zakłada się, że oryginalna lista była w rzeczywistości pełna instancji DerivedClass." W takim przypadku * działa *. Jeśli uważasz, że tak nie jest, musisz podać więcej szczegółów. –

+0

@Jon Myślę, że jesteś niekwestionowanym królem C# –

3

(powtarzane od here)

pierwsze - pamiętać, że może dodać konstruktorów (a inny kod) do klas WCF - wystarczy zrobić to w częściowej klasie (i pozostawić wygenerowany kod sam).

Wygląda na to, że typ typ pozycji na liście musi zostać zmieniony - nie możemy po prostu rzucić. Odbicie jest opcją, ale jest powolne. Ponieważ używasz 3.5, możemy być może napisać Expression zrobić to dla nas bardziej efektywnie ... wzdłuż these lines, ale stosując drugą klasę też:

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

} 

Kod ten będzie nie tylko konwertować, ale obejmują również tylko wystąpienia, które SĄ klas pochodnych i wykluczają te, które nie są.

2

Jak inni sugerowali, jeśli masz instancję List<BaseClass> można przekonwertować go do List<DerivedClass> metodą ListConvertAll.

Ogólniej, jeśli masz coś, który implementuje IEnumerable<T> (które nie ma metody ConvertAll) można używać LINQ na Cast zrobić to samo:

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

Jeśli potrzebujesz do List plecach zamiast z IEnumerable, możesz po prostu połączyć się z numerem ToList() na końcu.

Jak powiedział Jon, to wszystko przy założeniu, że wszystkie wpisy w listBaseClass są faktycznie typu DerivedClass.

Powiązane problemy