2009-02-05 12 views
12

Compte tenu des définitions de classe suivantes:Copie d'une liste <BaseClass> à la liste <ClasseDérivée>

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

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

Comment puis-je prendre un List<BaseClass> et le convertir en un List<DerivedClass>?

Dans mon scénario réel BaseClass a tout un tas de propriétés que je ne veux pas avoir à copier sur un par un (et puis n'oubliez pas de maintenir si une propriété supplémentaire est ajoutée).

L'ajout d'un constructeur paramétré à BaseClass n'est pas une option car cette classe est définie par une référence de service WCF.

+0

Est-ce que WCF ne génère pas de classes _partial_ dans le code proxy? –

+0

À un autre coup d'œil, vous n'avez pas dit si DerivedClass est uniquement une extension fonctionnelle au-dessus de la base ou si elle doit être une structure de données différente de celle suggérée par l'exemple de code. À mon humble avis, dans le deuxième cas, "conversion" n'a pas de sens. –

Répondre

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

En fait ConvertAll est bon quand vous avez besoin de créer de nouveaux objets en fonction de l'original, lorsque vous avez besoin pour vous lancer pouvez utiliser le

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

Si tous les éléments de votre liste peut être jeté à ClasseDérivée puis utiliser OfType à la place

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

Je vais regretter de vous avoir tenté de revenir ici, n'est-ce pas? –

+0

J'ai appris que de votre livre :-) –

+0

Cela ne semble pas fonctionner ... –

15

Vous ne pouvez pas convertir l'objet réel, mais il est facile de créer une nouvelle liste avec le contenu converti:

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

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

Ou si vous ne l'utilisez C# 3:

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

Cela suppose que la liste initiale était en fait plein d'exemples de ClasseDérivée. Si ce n'est pas le cas, modifiez le délégué pour créer une instance appropriée de DerivedClass basée sur la BaseClass donnée.

EDIT: Je ne sais pas pourquoi je ne viens pas poster une solution LINQ:

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

Cela ne semble pas fonctionner. Je pense que c'est parce que vous ne pouvez pas passer d'une classe de base à une classe dérivée? –

+0

@DanBailiff: Comme indiqué dans la réponse: "Cela suppose que la liste d'origine était réellement pleine d'instances de DerivedClass." Dans ce cas, cela * fonctionne *. Si vous ne le croyez pas, vous devrez donner plus de détails. –

+0

@Jon Je pense que vous êtes le roi incontesté de C# –

3

(répétées de here)

Première - Notez que vous pouvez ajouter constructeurs (et autre code) aux classes WCF - vous avez juste besoin de le faire dans une classe partielle (et laisser le code généré seul).

Il semble que le type des éléments de la liste doit être modifié - nous ne pouvons donc pas simplement lancer. La réflexion est une option, mais elle est lente. Puisque vous utilisez 3.5, nous pouvons peut-être écrire un Expression pour le faire pour nous de manière plus efficace ... le long these lines, mais en utilisant la seconde classe aussi:

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

} 

Ce code convertirons non seulement, mais incluez seulement les instances qui sont la classe dérivée et excluez celles qui ne le sont pas.

2

Comme d'autres l'ont suggéré, si vous avez une instance d'un List<BaseClass> vous pouvez le convertir en un List<DerivedClass> en utilisant la méthode ConvertAll de List.

Plus généralement, si vous avez quelque chose qui implémente IEnumerable<T> (qui n'a pas une méthode ConvertAll) vous pouvez utiliser LINQ de Cast pour faire la même chose:

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

Si vous avez besoin d'un List retour à la place d'un IEnumerable, vous pouvez simplement passer un appel à ToList() à la fin. Comme Jon a dit, cependant, tout suppose que toutes les entrées dans listBaseClass sont réellement de type DerivedClass.

Questions connexes