C # generics - могу ли я сделать так, чтобы T был одним из двух вариантов? - PullRequest
20 голосов
/ 21 июня 2010

Предположим, у меня есть следующая иерархия классов:

Class A {...}

Class B : A  {...}

Class C : A {...}

В настоящее время у меня есть

Class D<T> where T : A {...}

, но я бы хотел что-то в форме

Class D<T> where T in {B,C}

Это связано с некоторым странным поведением, я не отвечаю за то, что у B и C есть общие методы, которых нет в A, но было бы неплохо иметь возможность вызывать их в D на T.

Примечание: у меня нет доступа к A, B или C для их редактирования

Ответы [ 7 ]

24 голосов
/ 21 июня 2010

Вам необходимо определить интерфейс для общих методов, которые есть в B и C (давайте назовем его Ibc), заставить B и C реализовать этот интерфейс, а затем вы можете написать:

Class D<T> where T : A, Ibc {...}
9 голосов
/ 21 июня 2010

Это не возможно напрямую.

Как полагают другие, вы можете определить интерфейс и реализовать его в B и C.

Если это не вариант (например, если эти классы находятся вне вашего контроля), я могу предложить следующее: во-первых, начните с абстрактного класса, который включает в себя все функциональные возможности, которые вы можете получить с помощью любого T производного от A. Затем скажите, что у вас есть несколько методов, которые существуют как для B, так и для C, которые не являются частью A. В D вы можете сделать так, чтобы эти абстрактные методы были реализованы подклассами:

public abstract class D<T> where T : A
{
    protected T _member;

    public void DoSomethingAllTsCanDo()
    {
        _member.DoSomething();
    }

    public abstract void DoSomethingOnlyBAndCCanDo();
}

Затем вы можете наследовать от базового класса для каждого типа B и C и переопределить абстрактный (-ые) метод (-ы) для обеспечения соответствующей функциональности:

public class DB : D<B>
{
    public override void DoSomethingOnlyBAndCCanDo()
    {
        _member.DoSomethingOnlyBCanDo();
    }
}

public class DC : D<C>
{
    public override void DoSomethingOnlyBAndCCanDo()
    {
        _member.DoSomethingOnlyCCanDo();
    }
}
5 голосов
/ 21 июня 2010

Во-первых, если B и C имеют общие методы, это недостаток проекта, они не разделяют интерфейс.Тем не менее, вы можете исправить это, даже не имея доступа к B и C.
Можно создать общий интерфейс.Предположим, у вас есть:

public class A
{
}
public class B : A
{
    public void Start() { }
}
public class C : A
{
    public void Start() { }
}

Вы можете создать общий интерфейс:

public interface IStartable
{
    void Start();
}

и использовать его на производных классах от B и C:

public class BetterB : B, IStartable
{
}
public class BetterC : C, IStartable
{
}

Youможет не достичь этого, если вы получите экземпляры B и C как есть, но это можно рассмотреть, если вы создадите их.Фактически, со специализированными классами B и C вы можете использовать интерфейс вместо D<T>.

3 голосов
/ 21 июня 2010

B и C реализуют один и тот же интерфейс?Это может быть лучший маршрут.

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

Некоторые параметры:

  1. Создание интерфейса IderivedFromA, содержащего общие методы из B и C.
    Похоже, это невозможно из вашего вопроса
  2. В D приведите T к dynamic и вызовите методы динамически
    Самое простое решение, если вы можете использовать .Net 4
  3. В D проверяется, имеете ли вы дело с B или C, приведением и вызовом
    . Будет проверено компилятором и возможно из .Net 2
  4. Ответ Дана Тао : Создайте конкретную реализацию D<T> для B и C, они могут вызывать методы непосредственно из B и C.(Сам не думал об этом).
    Будет работать, только если «пользователь-источник» знает, что имеет дело с B или C, и не использует абстрактный A дляиспользуйте D<A>.Вместо этого следует использовать DB или DC.Но я думаю, что это так, иначе вам не нужны дженерики.
0 голосов
/ 21 июня 2010

Поскольку у вас нет доступа к источнику, единственным реальным ответом (если вы не хотите потерять безопасность при использовании dynamic) является явная проверка на B / C иотбрасывать.

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

Ограничение where в C # не позволяет указывать несколько классов в качестве выбора.Также, если вы укажете несколько где содержится, то они оба должны быть удовлетворены.Для ограничения нет логики ИЛИ.Вот спецификация: http://msdn.microsoft.com/en-us/library/bb384067.aspx

Ответы от Грзенио кажутся вам подходящими.Извлеките общее поведение в общий интерфейс для B и C. Затем вы можете использовать этот интерфейс как ограничение.

...