2009-05-20 2 views
1

Как я могу сделать следующий код работы? Я не думаю, что я вполне понимаю C# generics. Возможно, кто-то может указать мне в правильном направлении.Как создать общий класс с наследованием?

public abstract class A 
    { 
    } 

    public class B : A 
    { 
    } 

    public class C : A 
    { 
    } 

    public static List<C> GetCList() 
    { 
     return new List<C>(); 
    } 

    static void Main(string[] args) 
    { 
     List<A> listA = new List<A>(); 

     listA.Add(new B()); 
     listA.Add(new C()); 

     // Compiler cannot implicitly convert 
     List<A> listB = new List<B>(); 

     // Compiler cannot implicitly convert 
     List<A> listC = GetCList(); 

     // However, copying each element is fine 
     // It has something to do with generics (I think) 
     List<B> listD = new List<B>(); 
     foreach (B b in listD) 
     { 
      listB.Add(b); 
     } 
    } 

Это, вероятно, простой ответ.

Обновление: Во-первых, это невозможно в C# 3.0, но это возможно в C# 4.0.

Чтобы получить это работает в C# 3.0, который просто обходной путь до 4.0, используйте следующее:

 // Compiler is happy 
     List<A> listB = new List<B>().OfType<A>().ToList(); 

     // Compiler is happy 
     List<A> listC = GetCList().OfType<A>().ToList(); 
+0

На самом деле это не характерно для дженериков, но ответ Спенса примерно так же хорош, как и вы. C# 4.0 добавит «утиную печать», где вы можете уйти от такого рода вещей. – womp

+5

Это неверно. Мы НЕ добавляем утиную печать, а ковариация и контравариантность применимы только к интерфейсам; Список - это класс. –

ответ

3

вы всегда можете сделать это

List<A> testme = new List<B>().OfType<A>().ToList(); 

В "Боян Резник" отметил, вы также можете сделать ...

List<A> testme = new List<B>().Cast<A>().ToList(); 

разница отметить, что Cast <T>() не будет выполнено, если один или несколько типов не совпадают. Где OfType <T>() возвращает IEnumerable <T>, содержащий только те объекты, которые могут быть конвертированы

+0

Спасибо. Это дает возможность в C# 3.0. Однако есть ли дополнительная обработка/накладные расходы при дополнительных звонках? – kevindaub

+0

Да ... но если бы вы сделали его IEnumerable или IQueryable, это было бы не так плохо. –

+0

oftype требует, чтобы вы перечислили список, не правда ли? Если у вас есть контравариантность, то вызовы метода могут быть связаны системой типов не через перечисление и будут значительно быстрее, если вам не нужна броска? – Spence

4

Причина это не работает, потому что это не может быть определена, чтобы быть безопасным. Предположим, у вас есть

List<Giraffe> giraffes = new List<Giraffe>(); 
List<Animal> animals = giraffes; // suppose this were legal. 
// animals is now a reference to a list of giraffes, 
// but the type system doesn't know that. 
// You can put a turtle into a list of animals... 
animals.Add(new Turtle()); 

И эй, вы просто положить черепаху в список жирафов, и целостность системы типа теперь была нарушена. Вот почему это незаконно.

Ключом здесь является то, что «животные» и «жирафы» относятся к ОДНОМУ ОБЪЕКТУ, и этот объект представляет собой список жирафов. Но список жирафов не может делать столько, сколько может сделать список животных; в частности, он не может содержать черепаху.

+0

Я думаю, я просто не понимаю, почему незаконно размещать B и C в их абстрактном базовом классе. – kevindaub

+0

Мне также трудно понять это: вы помещаете черепаху в список животных, который, таким образом, случается только для того, чтобы содержать жирафов. Почему это должно быть проблемой? И Черепахи, и Жирафы будут делиться членами Животного, верно? Компилятор должен жаловаться, когда вы пытаетесь перечислить список животных в список жирафов. – Dabblernl

+0

Это было бы иначе, чем просто попытка собрать коллекцию животных, чтобы вы могли называть абстрактным методом .Walk() на всех из них. Если вы хотите выполнять общую работу и не смешивать типы, это не проблема. Вот почему, вероятно, лучше всего просто создать новый список или использовать только список как IEnumerable <> –

Смежные вопросы