使用Generic Cast to Interface的C#Factory方法

时间:2012-12-26 03:21:10

标签: c# generics factory-pattern

我有以下课程:

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

这种“向上演员”是如何正确实现的?

3 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:3)

为了使这个演员成为可能,您需要将接口类型参数标记为out

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

然后

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

但是这会给界面带来一些限制。请阅读out (Generic Modifier) (C# Reference)了解详情。

  

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

     
      
  1. type参数仅用作接口方法的返回类型,不用作方法参数的类型。
  2.   
  3. 类型参数不用作接口方法的通用约束。
  4.   

答案 2 :(得分:0)

考虑对容器使用Inversion of Control方法。各种实现在容器中注册自己。解析器请查询(x)的实例。 将Unity视为许多IOC容器工具中的一个。