如何使用泛型按类型创建类的实例

本文关键字:实例 创建 类型 何使用 泛型 | 更新日期: 2023-09-27 18:36:06

我在使用泛型按类型创建类的新实例时遇到问题。 我正在尝试做的是拥有一个可以使用DBSet<>或FakeDBSet<>创建的数据库上下文。 FakeDBSet将用于测试代码。 我目前有一个完整的假数据上下文,但这是一种浪费,因为唯一真正的区别是使用的 DBSet。 我已经研究过使用Activator.CreateInstance()没有太多运气。

例:

public class Album {}
public class Artist {}
public class MusicStoreContext
{
    public IDbSet<Album> Albums { get; set; }
    public IDbSet<Artist> Artists { get; set; }
    public MusicStoreContext(Type dbSetType)
    {
        Albums = new (dbSetType)<Album>;
        Artists = new (dbSetType)<Artist>;
    }
}
public class Startup
{
    public Startup()
    {
        // Production code would do something like this:
        MusicStoreContext context = new MusicStoreContext(typeof(DbSet<>));
        // Test code would do something like this:
        MusicStoreContext testContext = new MusicStoreContext(typeof(FakeDbSet<>));
    }
}

我也尝试过这样的事情:

public class MusicStoreContext<T> where T : IDBSet
{
    public IDbSet<Album> Albums { get; set; }
    public IDbSet<Artist> Artists { get; set; }
    ...

以下是我根据乔恩的建议提出的有效方法:

public class MusicStoreContext
{
    private IDbSet<Album> _Albums;
    private IDbSet<Artist> _Artists;
    public IDbSet<Album> Albums { get {return _Albums;} }
    public IDbSet<Artist> Artists { get {return _Artists; }
    public MusicStoreContext(Type dbSetType)
    {
        Albums = new (dbSetType)<Album>;
        Artists = new (dbSetType)<Artist>;
    }
    public TaxDocumentsContext() : base() 
    {
        CreateDbSets(new ProductionDbSetProvider());
    }
    public TaxDocumentsContext(IDbSetProvider provider)
    {
        CreateDbSets(provider);
    }
    private void CreateDbSets(IDbSetProvider provider)
    {
        provider.CreateDbSet<Album>(this, ref _Albums);
        provider.CreateDbSet<Artist>(this, ref _Artists);
    }
}

对于 DbSetProvider:

public interface IDbSetProvider
{
    void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class;
}
public class FakeDbSetProvider : IDbSetProvider
{
    public void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class
    {
        dbSet = new FakeDbSet<T>();
    }
}
public class ProductionDbSetProvider : IDbSetProvider
{
    public void CreateDbSet<T>(DbContext context, ref IDbSet<T> dbSet) where T : class
    {
        dbSet = context.Set<T>();
    }
}

现在,我能够从这里使用FakeDbSet轻松测试而无需命中数据库:http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

如何使用泛型按类型创建类的实例

编辑:如果所讨论的集合具有适当的构造函数,则可以使用 Type.MakeGenericType 创建相关的构造类型并使用 Activator.CreateInstance 调用构造函数:

Type albumType = dbSetType.MakeGenericType(typeof(Album));
Albums = (IDbSet<Album>) Activator.CreateInstance(albumType);

或者,您可以传入具有通用方法的DbSetProvider(或其他任何内容):

public IDbSet<T> CreateDbSet<T>()

那么你将有一个ProductionDbSetProvider和一个FakeDbSetProvider

public MusicStoreContext(DbSetProvider provider)
{
    Albums = provider.CreateDbSet<Album>();
    Artists = provider.CreateDbSet<Artist>();
}

就我个人而言,这对我来说感觉更干净,但是 YMMV。