【问题标题】:C#: Construct Type Constrained Half-Closed Generic Implementations of Open Generic Interface via Open Generic Method/ClassC#:通过开放通用方法/类构造开放通用接口的类型约束半封闭通用实现
【发布时间】:2019-10-06 04:56:04
【问题描述】:

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

以下是该场景的综合示例:

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&lt;T&gt;(); 模式匹配的行中,我不知道如何让 c# 编译器将泛型类型“T”传递给它,因为我知道它将完全满足类型约束。我确信这可以通过反射来完成,但我只找到了有关如何构建方法和属性的信息,而不是泛型类。这是如何实现的?

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

【问题讨论】:

    标签: generics reflection interface dependencies open-generics


    【解决方案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>();
            }
    
            // ...
        }
    

    【讨论】:

    • 这对抽象类有用吗?我似乎无法让它工作。我收到“无法投射”异常。我不认为我们可以从基类转换为子类(即从 SqlServerDbModel 转换为 User 将不起作用)。
    【解决方案2】:

    把问题写出来很有帮助,而且事实证明,这比我最初预期的要容易。 @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 来创建对象,然后将其转换回正确的类型。我希望有一种方法可以在温莎城堡“本地”执行此操作,而无需借助自定义工厂/反射。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      相关资源
      最近更新 更多