c# 3.0 Casting an interfaced generic type
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 AverageQ开发者_开发百科ueueTimeRepository
{
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 IStatisticsRepository
is covariant in T
:
public interface IStatististicsRepository<out T> : IRepository<T>
where T : IStastistic
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, if IRepository<T>
contains a method like this:
void Save(string id, T value);
then it wouldn't be safe, because you'd be able to write:
IStatisticsRepository<IStatistic> repo = RepositoryFactory.AverageCheckTimeRepository;
IStatistic foo = new SomeOtherStastisticType();
repo.Save("Foo", foo);
That would be trying to save a SomeOtherStatisticType
value in an AverageCheckTimeRepository
, which violates type safety. It's only safe to make the interface covariant in T
if values of type T
only 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.
精彩评论