3

我有以下课程:

// -- 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 {
}

我从工厂方法调用以下代码,这似乎不起作用:

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
}

这种“上投”是如何正确实现的?

4

3 回答 3

4

使IJobDao接口协变:

public interface IJobDao<out T> where T : IJob
{
    T findById(long jobId);
}

更新:

您不能同时让接口方法返回和接受泛型值并使其成为协变的。

可能的解决方案:

  • 创建一个非通用版本IJobDao<T>- IJobDao(当然,你必须在类中实现这两个接口,实现IJobDao<T>
  • 分成IJobDao<T>2 个接口(一个协变和一个逆变)
  • 考虑一个只有非泛型接口的解决方案IJobDao(无论如何,你在这里没有得到任何类型安全,这是泛型的主要目的)

关于实现第一个场景的一些想法:

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;
}
于 2012-12-26T03:31:44.247 回答
3

为了使这种转换成为可能,您需要将接口类型参数标记为out

public interface IJobDao<out T> where T : IJob {...}

然后

object jobDao = Activator.CreateInstance(typeof(FullTimeJobDaoImpl));
var r = jobDao as IJobDao<IJob>; //not null

但这给界面带来了一些限制。阅读(通用修饰符)(C# 参考)以获取更多信息。

在泛型接口中,如果类型参数满足以下条件,则可以将其声明为协变:

  1. 类型参数仅用作接口方法的返回类型,不用作方法参数的类型。
  2. 类型参数不用作接口方法的通用约束。
于 2012-12-26T03:29:34.720 回答
0

考虑对容器使用控制反转方法。各种实现在容器中注册自己。解析器查询 (x) 的一个实例。将 Unity 视为众多 IOC 容器工具之一。

于 2012-12-26T03:54:04.307 回答