Given these base classes and interfaces
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>(); }
}
Why does AverageCheckTime’s implementation throw an invalid cast exception:
protected override IStatisticsRepository<IStatistic> Repository
{
get { return (IStatisticsRepository<IStatistic>)RepositoryFactory.AverageCheckTimeRepository; }
}
How do I cast an instance of IAverageCheckTimeRepository as an IStatisticsRepository<IStatistic> which I assumed it already was?
OK, I’ve made these changes…which makes me wonder if I’ve gone over the top with the generics in the first place
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; }
}
No, C# 3 does not support generic variance. C# 4 does, but you would have to declare that
IStatisticsRepositoryis covariant inT:Variance isn’t safe in general – it depends on how the generic type parameter is used. C# 4 supports both covariance and contravariance for type arguments which are reference types, but only when the generic type involved is an interface or a delegate, and only when the type parameter is used in the appropriate way within the interface/delegate.
Without seeing the declaration for
IRepository<T>, we can’t tell whether or not it’s safe. For example, ifIRepository<T>contains a method like this:then it wouldn’t be safe, because you’d be able to write:
That would be trying to save a
SomeOtherStatisticTypevalue in anAverageCheckTimeRepository, which violates type safety. It’s only safe to make the interface covariant inTif values of typeTonly come “out” of the interface. (There are some wrinkles around exactly what that means, mind you…)For a lot more information on this, see Eric Lippert’s blog series on the topic.