Подтвердить что ты не робот

С# generics - Могу ли я сделать T одним из двух вариантов?

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

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, чтобы редактировать их.

4b9b3361

Ответ 1

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

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

Ответ 2

Это невозможно.

Как показывают другие, вы можете определить интерфейс и реализовать его как в 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();
    }
}

Ответ 3

Во-первых, если у 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
{
}

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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