c # 3.0 Приведение сопряженного универсального типа - PullRequest
1 голос
/ 29 сентября 2011

С учетом этих базовых классов и интерфейсов

public abstract class Statistic : Entity, IStatistic
{
  protected abstract IStatisticsRepository<IStatistic> Repository {get;}

...

public class AverageCheckTime : Statistic

...

public interface IStatisticsRepository<T> : IRepository<T>  where T : IStatistic

...

public interface IAverageCheckTimeRepository : IStatisticsRepository<AverageCheckTime>

...

public class AverageCheckTimeRepository : StatisticRepository<AverageCheckTime>, IAverageCheckTimeRepository

...

public class RepositoryFactory
{
   public static IAverageQueueTimeRepository AverageQueueTimeRepository 
    {
      get { return CurrentServiceLocator.GetInstance<IAverageQueueTimeRepository>(); }
    }

Почему реализация AverageCheckTime выдает недопустимое исключение приведения:

protected override IStatisticsRepository<IStatistic> Repository
    {
      get { return (IStatisticsRepository<IStatistic>)RepositoryFactory.AverageCheckTimeRepository; }
    }

Как я могу разыграть экземпляр IAverageCheckTimeRepository как IStatisticsRepository<IStatistic>, который, как я предполагал, уже был?


Хорошо, я внес эти изменения ..., что заставляет меня задаться вопросом, перешел ли я с дженериками в первую очередь

    public interface IStatisticsHelper
      {
        void GenerateStatistics();

        List<IStatistic> BuildReport();
      }

...

    public interface IStatisticsRepository<T> : IRepository<T>, IStatisticsHelper where T : IStatistic
      {

      }

...

    public abstract class Statistic : Entity, IStatistic
      {

        protected abstract IStatisticsHelper Repository { get; }

    ...

public class AverageCheckTime : Statistic
  {
    protected override IStatisticsHelper Repository
    {
      get { return RepositoryFactory.AverageCheckTimeRepository; }
    }

1 Ответ

5 голосов
/ 29 сентября 2011

Нет, C # 3 не поддерживает универсальную дисперсию .C # 4 делает, но вы должны объявить, что IStatisticsRepository является ковариантным в T:

public interface IStatististicsRepository<out T> : IRepository<T>
    where T : IStastistic

Дисперсия небезопасна в общем - это зависит от того, как универсальныйПараметр типа используется.C # 4 поддерживает как ковариацию, так и контравариантность для аргументов типа, которые являются ссылочными типами, но только тогда, когда задействованный универсальный тип является интерфейсом или делегатом, и только когда параметр типа используется соответствующим образом в интерфейсе / делегате.

Не видя декларации для IRepository<T>, мы не можем сказать, безопасно ли это.Например, если IRepository<T> содержит такой метод:

void Save(string id, T value);

, тогда не будет безопасным, потому что вы сможете написать:

IStatisticsRepository<IStatistic> repo = RepositoryFactory.AverageCheckTimeRepository;
IStatistic foo = new SomeOtherStastisticType();
repo.Save("Foo", foo);

Это будет попытка сохранить значение SomeOtherStatisticType в AverageCheckTimeRepository, что нарушает безопасность типов.Ковариантность интерфейса можно сделать только в T, только если значения типа T выходят только из интерфейса.(Есть некоторые морщины вокруг именно того, что это значит, заметьте ...)

Для получения дополнительной информации об этом см. Серию блогов Эрика Липперта на тему .

...