C#:通过开放通用方法/类

时间:2019-05-19 17:49:09

标签: generics reflection interface dependencies open-generics

我正在尝试构造一个开放的通用存储库接口的实例,由此实现比该接口施加更严格的类型约束。存储库接口的每个实现都需要通用类型的特定实现,以根据传递的类型的属性来处理某些方法/操作(为简洁起见,未显示)。

这是该场景的综合示例:

public interface IRepository<T> where T : class
{
    //...
}

public class BaseRepository<T> : IRepository<T> where T : DbModel
{
    //...
}

public class SqlServerDbRepository<T> : BaseRepository<T> where T : SqlServerDbModel
{
    //...
}

public abstract class DbModel
{
    //...
}

// is further derived by other models
public class SqlServerDbModel : DbModel
{
    //...
}

public class User : SqlServerDbModel
{
}

// CLIENT CODE

public static IRepository<T> BuildRepository<T>()
    where T : class
{
    if (typeof(T) == typeof(SqlServerDbModel)) // "is" keyword will not work here (according to IDE, have not checked)
    {
        return new SqlServerDbRepository<T>(); // How can T be converted or accepted as an input of type "SqlServerDbModel" (the check already confirms it, so we know it should work)
    }
    else if (typeof(T) == typeof(DbModel))
    {
        return new BaseRepository<T>(); // How can T be converted or accepted as an input of type "DbModel" (the check already confirms it, so we know it should work)
    }
    //... else throw error or just return default...
}

// USAGE
public static void TestBuildRepository()
{
    var userRepository = BuildRepository<User>();
}

我最初尝试通过IOC容器(如果有任何人想知道的话,是Castle Windsor)运行该方案,弄清楚它会自动找出类型约束,但是,这是不可能的(或者至少不是通过它处理开放泛型的方式)和依赖注入)。我想我可以使用自定义工厂来构建接口实现。

问题出在匹配模式return new XYZRepository<T>();的行中,由此我不确定如何知道要完全满足类型约束的条件,如何使c#编译器采用传递给它的通用类型“ T”。我确信这可以通过反思来完成,但是我只找到了有关如何构建方法和属性的信息,而不是泛型类的信息。如何实现?

我无法对接口,存储库实现或模型进行任何更改,以防万一有人提出该建议。

2 个答案:

答案 0 :(得分:1)

我认为您正在寻找这样的东西:

    public static IRepository<T> BuildRepository<T>() where T : class
    {
        if (typeof(T) == typeof(SqlServerDbModel))
        {
            return (IRepository<T>)new SqlServerDbRepository<SqlServerDbModel>();
        }

        if (typeof(T) == typeof(DbModel))
        {
            return (IRepository<T>)new BaseRepository<DbModel>();
        }

        // ...
    }

答案 1 :(得分:0)

这有助于解决问题,事实证明,这比我最初预期的要容易。 @CRAGIN的答案给了我最后一个缺失的部分(据...哦,是的,我们可以转换为C#接口)。

万一未来的人跌跌撞撞...

public static IRepository<T> BuildRepository<T>(params object[] constructor_arguments)
    where T : class
{
    if (typeof(T) == typeof(SqlServerDbModel))
    {
        return (IRepository<T>)Activator.CreateInstance(typeof(SqlServerDbRepository<>).MakeGenericType(typeof(T)), constructor_arguments);
    }
    else if (typeof(T) == typeof(DbModel))
    {
        return (IRepository<T>)Activator.CreateInstance(typeof(BaseRepository<>).MakeGenericType(typeof(T)), constructor_arguments);
    }
    //... else throw error or just return default...
}

我需要使用Activator.CreateInstance API制作对象,然后将其强制转换回正确的类型。我希望在温莎城堡(Castle Windsor)中有一种方法可以“自然地”完成而无需诉诸定制工厂/反射。