Общий метод с дисперсией в C #? - PullRequest
0 голосов
/ 25 апреля 2011

Рассмотрим следующие классы (дерево наследования):

public class A {}
public class B: A {}

И этот метод:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

Компилятор недоволен. Ошибка Cannot convert expression type IList<B> to return type IList<A>. Как мне это решить? Другими словами, как указать, что MyMethod будет возвращать IList<T> из T, где T может быть любым, что наследуется от A или экземплярами самого A?

Ответы [ 3 ]

4 голосов
/ 25 апреля 2011

То, что вы просите, невозможно, потому что IList<T> не поддерживает дисперсию - вы не можете использовать IList<B> везде, где ожидается IList<A>.Вам нужно будет объяснить более подробную информацию о том, что вы хотите, чтобы найти решение.

Возможные решения:

public IList<A> MyMethod(){
    IList<A> result = new List<A>();

    //add some items to result

    return result;
}

Или

public IEnumerable<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}
2 голосов
/ 25 апреля 2011

Нельзя преобразовать IList в IList , даже если B наследуется от A. В противном случае пользователь может попытаться добавить в список экземпляр A, не являющийся B.

public void Example(){
    IList<B> listB = new List<B>();
    IList<A> listA = listB;
    listA.Add(new A()); // Can't insert A into a list of B
}

Можете ли вы вернуть IEnumerable вместо IList ?IEnumerable является ковариантным, в отличие от IList .

1 голос
/ 14 октября 2012

как указать, что MyMethod будет возвращать IList из T, где T может быть всем, что наследует от A или экземплярами самого A?


Вам не нужно

Вы можете просто объявить, что он возвращает IList<A>.Зачем?Поскольку - учитывая, что B наследуется от A - каждый элемент B может быть передан там, где запрашиваемый тип A.

Назовите это полиморфизмом по наследованию, принципу подстановки Лискова или дисперсии методаимя не имеет значения.Важно то, что следующие работы (проверенные на LinqPad):

public class A {}
public class B: A {}

public IList<A> MyMethod()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new B());

    return result;
}

Генетические альтернативы

Фактически, вы можете сказать, что вы собираетесь вернуть IList<TA> изапросите несколько производных типов (TB, TC ...), чтобы заполнить его.Правильно, следующий пример также работает (проверено на LinqPad):

void Main()
{
    MyMethod<A, B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<TA> MyMethod<TA, TB, TC>()
    where TB : TA, new()
    where TC : TA, new()
    where TA : class
{
    var result = new List<TA>();

    //add some items to result
    result.Add(new B() as TA);
    result.Add(new C() as TA);

    return result;
}

Или, если вы хотите сохранить определенный базовый тип (скажем, вы хотите вернуть IList<A>, но он на самом деле содержит элементы классовчто происходит от A, то вы можете сделать это:

void Main()
{
    MyMethod<B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<A> MyMethod<T1, T2>()
    where T1 : A, new()
    where T2 : A, new()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new T1() as A);
    result.Add(new T2() as A);

    return result;
}

Вам не нужно, но вы можете

ОК, если вы действительно хотите сказать этовозвращает IList<T> where T : A. Затем скажите, что!

void Main()
{
    MyMethod<B>();
}

public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class

public IList<T> MyMethod<T>()
    where T : A, new()
{
    var result = new List<T>();

    //add some items to result
    result.Add(new T());

    return result;
}

Да, что нельзя возвращать смесь элементов типа T и элементов типа A, потому что он говорит, что возвращает IList<T> и не каждый элемент типа A также является элементом типа T.


Что происходит с вашим кодом

Посмотрите на ваш код:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

Вы пытаетесь вернуть IList<B>, когда говорите, что собираетесь вернуть IList<A>. Предположим, что это работает ... тогда что будет с вызывающей стороной вашего метода? Давайте посмотрим:

public class A {}
public class B: A {}
public class C: A {}

void Main()
{
    //Hmmm... I need a IList<T>, let's call MyMethod!
    IList<A> list = MyMethod();
    //Cool, I got an IList<A>, now let's add some items...
    var item = new C();
    //Well, item is of type C...
    // and C inherits from A, so I must be able to add it...
    list.Add(item); //BOOM!
    //It was actually an IList<B>!
    // and C doesn't dervive from B, so you can't add it.
}

DFTBA!

...