Reputation: 10476
I have the following classes:
// -- model hierarchy
public interface IJob {
}
public abstract class AbstractJob : IJob {
}
public class FullTimeJob : AbstractJob {
}
// -- dao hierarchy
public interface IJobDao<T> where T : IJob {
T findById(long jobId);
long insert(T job);
}
public interface IFullTimeJobDao : IJobDao<FullTimeJob> {
}
public abstract class AbstractDao {
}
public abstract class AbstractJobDaoImpl<T> : AbstractDao, IJobDao<T> where T : IJob {
public T findById(long jobId) {
// omitted for brevity
}
public long insert(T job) {
// omitted for brevity
}
}
public class FullTimeJobDaoImpl : AbstractJobDaoImpl<FullTimeJob>, IFullTimeJobDao {
}
I'm calling the following code from a factory method, which does not seem to work:
public IJobDao<IJob> createJobDao(long jobDaoTypeId)
{
object jobDao = Activator.CreateInstance(typeof(FullTimeJobDaoImpl));
return jobDao as IJobDao<IJob>; // <-- this returns null
return (IJobDao<IJob>) jobDao; // <-- this cast fails
}
How is this "up cast" properly achieved?
Upvotes: 3
Views: 2878
Reputation: 18534
For this cast to be possible you'll need to mark the interface type parameter as out
:
public interface IJobDao<out T> where T : IJob {...}
Then
object jobDao = Activator.CreateInstance(typeof(FullTimeJobDaoImpl));
var r = jobDao as IJobDao<IJob>; //not null
But this brings some restrictions on the interface. Read out (Generic Modifier) (C# Reference) for more info.
In a generic interface, a type parameter can be declared covariant if it satisfies the following conditions:
- The type parameter is used only as a return type of interface methods and not used as a type of method arguments.
- The type parameter is not used as a generic constraint for the interface methods.
Upvotes: 3
Reputation: 11328
Consider using Inversion of Control approach with a container. The various implementations register themselves in the container. The resolver enquires please an instance of (x). See Unity as 1 of many IOC Container tools.
Upvotes: 0
Reputation: 34407
Make IJobDao
interface covariant:
public interface IJobDao<out T> where T : IJob
{
T findById(long jobId);
}
Update:
You cannot have interface methods both returning and accepting generic values and make it covariant at the same time.
Possible solutions:
IJobDao<T>
- IJobDao
(of course, you'll have to implement both interfaces in classes, implementing IJobDao<T>
)IJobDao<T>
into 2 interfaces (one covariant and one contravariant)IJobDao
(you are not getting any type-safety here anyway, which is the main purpose of generics)Some ideas on implementing first scenario:
public interface IJobDao
{
IJob findById(long jobId);
long insert(IJob job);
}
public interface IJobDao<T> : IJobDao
where T : IJob
{
new T findById(long jobId);
new long insert(T job);
}
public abstract class JobDaoBase<T> : IJobDao<T>, IJobDao
where T : IJob
{
public abstract T findById(long jobId);
public abstract long insert(T job);
IJob IJobDao.findById(long jobId)
{
return findById(jobId);
}
long IJobDao.insert(IJob job)
{
return insert((T)job);
}
}
public class FullTimeJobDaoImpl : JobDaoBase<FullTimeJob>
{
public override FullTimeJob findById(long jobId)
{
// implementation
}
public override long insert(FullTimeJob job)
{
// implementation
}
}
// we are still unable to return generic interface, but we don't need to.
public static IJobDao createJobDao(/* my params */)
{
object jobDao = Activator.CreateInstance(typeof(FullTimeJobDaoImpl));
return jobDao as IJobDao;
}
Upvotes: 4