Выводы типа для метода, с обобщениями и наследованием классов - PullRequest
4 голосов
/ 02 ноября 2011

У меня есть иерархия классов, которая выглядит следующим образом:

class Base<TElement>
{
    public TElement Element { get; set; }
}

class Concrete : Base<string>
{
}

Я хотел бы написать метод, который принимает Base подклассы:

public TConcrete DoSomething<TConcrete, TElement>()
    where TConcrete : Base<TElement>
{
}

Есть ли способопределить DoSomething, не определяя TElement?

Идеальным решением было бы, если бы компилятор мог автоматически вычислять TElement, поэтому вызывающий код будет выглядеть следующим образом:

var item = DoSomething<Concrete>();

Я использую C # 4.0.

Ответы [ 3 ]

4 голосов
/ 02 ноября 2011

Это невозможно по следующим причинам:

  1. Начиная с C # 4, вывод типа «все или ничего» - компилятор не может выводить некоторые общие аргументы, но не другие.
  2. Начиная с C # 4, невозможно указать универсальные «подстановочные знаки», такие как where TConcrete : Base<???>.

Вот несколько обходных путей.

Неуниверсальный базовый тип : Создать базовый класс или тип интерфейса, который не универсальный. Это обычная модель; например IEnumerable<T> : IEnumerable.


Ковариантный интерфейс : С помощью ковариации универсального интерфейса C # 4 вы можете создать безопасное для типов решение, которое не требует загромождать ваши типы "уродливыми" неуниверсальными элементами:

public interface IBase<out TElement>
{
    TElement Element { get; }
}

class Base<TElement> : IBase<TElement>
{
    public TElement Element { get; set; }
}

class Concrete : Base<string>  {  }

А потом:

// Won't work with value types.
public TConcrete DoSomething<TConcrete>()
    where TConcrete : IBase<object> { }

И назовите это как:

var item = DoSomething<Concrete>();
1 голос
/ 02 ноября 2011

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

В противном случае, нет.Если бы это было возможно, свойство TConcrete.Element внутри вашего метода не имело бы типа.
Что бы произошло, если вы напишите

public TConcrete DoSomething<TConcrete>() where TConcrete : Base<>   //Illegal!
{
    TConcrete c = ...;
    var b = c.Element;    //What type is that variable?
}
0 голосов
/ 02 ноября 2011

Если DoSomething не знает (или не заботится), что такое TElement, возможно, вы захотите создать родительский класс без параметра типа:

class Base
{
}

class Base<TElement> : Base
{
    public TElement Element { get; set; }
}

Тогда ваш DoSomething метод будет работать в классе Base.

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

...