Тип параметра "T" имеет то же имя, что и параметр типа из внешнего типа "..."

public abstract class EntityBase { ... }

public interface IFoobar
{
    void Foo<T>(int x)
        where T : EntityBase, new();
}

public interface IFoobar<T>
    where T : EntityBase, new()
{
    void Foo(int x);
}

public class Foobar<T> : IFoobar, IFoobar<T>
    where T : EntityBase, new()
{
    public void Foo(int x) { ... }

    void IFoobar.Foo<T>(int x) { Foo(x); }
}

Я получаю предупреждение о компиляторе: Type parameter 'T' has the same name as the type parameter from outer type '...'

Я пробовал делать: void IFoobar.Foo<U>(int x) { Foo(x); }, но тогда я не могу гарантировать, что U и T одинаковы. То, как реализуется класс Foobar, очень важно, чтобы они были одинаковыми.

Я также попытался сделать: void IFoobar.Foo<U>(int x) where U : T { Foo(x); }, однако это не гарантирует, что U и T равны, и это не позволяет мне переопределить ограничение, поскольку оно было определено на интерфейсе.

Ответы

Ответ 1

Вы можете сделать одну из двух вещей:

  • Игнорировать предупреждение и сделать оба типа T.
  • Проделайте проверку времени выполнения и выполните исключение:

    if (typeof(T) != typeof(U)) throw Exception("Not the same type");
    

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

Ответ 2

Самая большая проблема заключается в том, что ваши интерфейсы недостаточно четко определены и не соответствуют намерению вашего кода.

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

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

Если окажется, что вам нужна версия, которая принимает T и не-T-версию, то более идиоматический способ сделать это - это пройти object вместо T:

public interface IFoo
{
    void DoSomething(object o);
    object DoSomethingElse();
}

public interface IFoo<T>
{
    void DoSomething(T item);
    T DoSomethingElse();
}

Для примера см. такие интерфейсы, как IEnumerable, ICollection, IList и т.д.

Но внимательно рассмотрите. Этот последний компромисс дизайна (имеющий как общую, так и объектную версию) всегда оставляет желать лучшего.

Вы пожертвуете одним из них:

  • Хороший дизайн интерфейса, который напрямую связывает контракт с дизайном (если вы бросаете исключения или делаете no-op при вводе неправильного типа)
  • Введите безопасность и уменьшите количество ошибок, связанных с ним (если вы правильно работаете с любым старым объектом)

Ответ 3

Просто попробуйте

void IFoobar.Foo<U>(int x) { Foo(x); }

Конечно, это все равно не гарантирует, что U совпадает с T. Вы не можете применять во время компиляции, потому что когда вы реализуете интерфейс, вы должны следовать своим правилам - и IFoobar не ставит такие ограничение на Foo<T>, и если вы это сделаете, вы больше не будете реализовывать интерфейс (по определению, поскольку вы строже, и все же вы утверждаете, что это не так).

Вместо этого вы можете проверить его во время выполнения, хотя это несколько "обманывает" (так как вы на самом деле не соответствуете интерфейсу).