C#:通过 Open Generic Method/Class 构造类型约束的开放通用接口的半封闭通用实现
C#: Construct Type Constrained Half-Closed Generic Implementations of Open Generic Interface via Open Generic Method/Class
我正在尝试构建一个开放的通用存储库接口的实例,其中实现强加了比接口更严格的类型约束。存储库接口的每个实现都需要通用类型的特定实现,以根据传递的类型的属性(为简洁起见未显示)处理某些 methods/operations。
下面是场景的综合示例:
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",知道它会完全满足类型约束。我确信这可以通过反射来完成,但我只找到了关于如何构建方法和属性的信息,而不是通用的 类。这是如何实现的?
我无法对接口、存储库实现或模型进行任何更改...以防万一有人提出该建议。
我想你正在寻找这样的东西:
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>();
}
// ...
}
把问题写出来很有帮助,事实证明,这比我原先预期的要容易。 @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 "natively" 中做到这一点,而无需借助自定义工厂/反射。
我正在尝试构建一个开放的通用存储库接口的实例,其中实现强加了比接口更严格的类型约束。存储库接口的每个实现都需要通用类型的特定实现,以根据传递的类型的属性(为简洁起见未显示)处理某些 methods/operations。
下面是场景的综合示例:
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",知道它会完全满足类型约束。我确信这可以通过反射来完成,但我只找到了关于如何构建方法和属性的信息,而不是通用的 类。这是如何实现的?
我无法对接口、存储库实现或模型进行任何更改...以防万一有人提出该建议。
我想你正在寻找这样的东西:
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>();
}
// ...
}
把问题写出来很有帮助,事实证明,这比我原先预期的要容易。 @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 "natively" 中做到这一点,而无需借助自定义工厂/反射。