Как создать общий список в этом странном случае в C # - PullRequest
2 голосов
/ 03 июня 2010

В моей программе у меня есть класс A, который расширен на B, C и многие другие классы. У меня есть метод GetInstance(), который возвращает экземпляр B или C (или одного из других дочерних элементов), но я не знаю, какой из них, поэтому тип возвращаемого метода - A.

В методе CreateGenericList() у меня есть переменная v типа A, которая на самом деле является либо B, C, либо другим дочерним типом, и я хочу создать общий список правильный тип, то есть List<B>, если v является B или List<C>, если v является C, ...

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

Вот пример кода моей проблемы:

class A { }  
class B : A { }  
class C : A { }
// More childs of A.


class Program
{
    static A GetInstance()
    {
        // returns an instance of B or C
    }

    static void CreateGenericList()
    {
        A v = Program.GetInstance();
        IList genericList = // Here I want an instance of List<B> or List<C> or ... depending of the real type of v, not a List<A>.
    }
}

Я попробовал следующий взлом. Я вызываю следующий метод, надеясь, что механизм вывода типов угадывает тип model, но он не работает и возвращает List<A>. Я считаю, что поскольку c # статически типизирован, T разрешается как A, а не как реальный тип model во время выполнения.

static List<T> CreateGenericListFromModel<T>(T model) where T : A
{
    return new List<T> ();
}

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

Большое спасибо.

Ответы [ 3 ]

5 голосов
/ 03 июня 2010

Вы не можете решить эту проблему без размышления, так как параметры универсального типа должны быть известны во время компиляции. Если вы знаете, какой у вас тип (например, if(v[0] is B)), вы можете использовать v.Cast<B>(), чтобы преобразовать его из List<A> в IEnumerable<B>, который вы можете снова превратить в список, вызвав ToList(). * 1006. *

2 голосов
/ 03 июня 2010

Мне кажется, что это немного пахнет кодом. Я бы посоветовал вам вернуться к своему дизайну, чтобы подумать, почему вам действительно нужно это сделать. Причина, по которой это не так просто, состоит в том, что то, что вы пытаетесь сделать, нарушает объектно-ориентированные принципы.

Если вы следуете принципам SOLID , ваш код никогда не должен знать, действительно ли то, что он считал классом А, было классом В или С.

Как будет иметь значение дальше, если ваш список B или C? У вас есть код вроде:

if (x is B)
{
  // do stuff to B
}
else
{
  // do stuff to C
}

Если ты это сделаешь, то будь осторожен. Избегайте этого! Что должно произойти, когда вы получите класс D, с которым вам также придется справиться?

Я бы посоветовал просто использовать Список А. Это все, что вам нужно сделать. Любые различия в функциональности между B или C должны обрабатываться переопределением методов, определенных в A ( Полиморфизм ).

0 голосов
/ 03 июня 2010

Чтобы расширить вопрос об ответе Femaref, вы пытаетесь сделать свой List<T> контравариантным по сравнению с заданием C # 4 действительно поддерживает ковариантные и контравариантные универсальные типы. однако ...

в C # 4 Список является ковариантным (вы можете назначить List<B> для List<A>, но не наоборот), прежде чем C # 4 List<A> и List<B> являются отдельными типами и не могут использоваться вместе вообще (они инвариантны над всеми операциями)

однако в c # 4 есть кое-что, что может вам помочь. динамичный! вы можете (AFAIK) создать List<dynamic> вместо вашего List<A>, что позволит вам получить доступ к каждому элементу списка в соответствии с его типом времени выполнения, а не временем компиляции. это по сути похоже на способ отражения вещей, но код, вероятно, будет немного аккуратнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...