C# 如何通过泛型类型获得接口的具体实现?
我需要一些帮助来了解如何使用反射来获得基于Dto类型的具体实现:C# 如何通过泛型类型获得接口的具体实现?,c#,reflection,C#,Reflection,我需要一些帮助来了解如何使用反射来获得基于Dto类型的具体实现: public interface IDocumentService<TDto> { } public interface ICommentService: IDoumentService<CommentDto> { } public abstract class DocumentService<TEntity,TDto>: IDocumentService<TDto> where
public interface IDocumentService<TDto>
{
}
public interface ICommentService: IDoumentService<CommentDto>
{
}
public abstract class DocumentService<TEntity,TDto>: IDocumentService<TDto> where TEntity: Entity, where TDto: Dto
{
}
public class CommentService: DocumentService<Comment,CommentDto>, ICommentService
{
}
公共接口IDocumentService
{
}
公共接口ICommentService:IDoumentService
{
}
公共抽象类DocumentService:IDocumentService,其中tenty:Entity,其中TDto:Dto
{
}
公共类CommentService:DocumentService、ICommentService
{
}
所以,我想做的是将CommentDto传递给一个方法,这样我就可以访问CommentService
public IDocumentService<TDto> GetDocumentService<TDto>()
{
//based on the TDto type I want to find the concrete that
//implements IDocumentService<TDto>
}
public static IDocumentService<TDto> GetDocumentService<TDto>()
{
// Gets the type for IDocumentService
Type tDto=typeof(IDocumentService<TDto>);
Type tConcrete=null;
foreach(Type t in Assembly.GetExecutingAssembly().GetTypes()){
// Find a type that implements tDto and is concrete.
// Assumes that the type is found in the executing assembly.
if(tDto.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface){
tConcrete=t;
break;
}
}
// Create an instance of the concrete type
object o=Activator.CreateInstance(tConcrete);
return (IDocumentService<TDto>)o;
}
public IDocumentService GetDocumentService()
{
//基于TDto类型,我想找到
//实现IDocumentService
}
我这样称呼它:
var commentDocumentService = GetDocumentService<CommentDto>();
var commentDocumentService=GetDocumentService();
因此,我将返回CommentService,因为我知道我只能看到IDocumentService接口的方法部分 首先,考虑到
TDto
的类型,您的CommentService
类需要以某种方式被发现。您可以从当前的AppDomain
中的所有程序集中搜索所有加载的类型,但是这将非常缓慢
因此,您有以下可行的选择:
- 在程序集上使用定义
的属性CommentService
- 使用配置来定义此信息
- 使用
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)]
public sealed class DtoProviderAttribute : Attribute
{
public Type ProvidedType { get; private set; }
public Type ProviderType { get; private set; }
public DtoProviderAttribute(Type providedType, Type providerType)
{
ProvidedType = providedType;
ProviderType = providerType;
}
}
然后将其应用于定义CommentService
的程序集(通常您会放入AssemblyInfo.cs
)
现在您可以使用这些属性来搜索具体的实现
public class ServiceFactory
{
private static readonly Dictionary<RuntimeTypeHandle, Func<object>> _dtoMappings = new Dictionary<RuntimeTypeHandle, Func<object>>();
public static IDocumentService<TDto> GetDocumentService<TDto>()
{
var rth = typeof(TDto).TypeHandle;
Func<object> concreteFactory;
lock (_dtoMappings)
{
if (_dtoMappings.TryGetValue(typeof(TDto).TypeHandle, out concreteFactory))
return (IDocumentService<TDto>)concreteFactory();
FillMappings();
if (!_dtoMappings.TryGetValue(typeof(TDto).TypeHandle, out concreteFactory))
throw new Exception("No concrete implementation found.");
return (IDocumentService<TDto>)concreteFactory();
}
}
private static void FillMappings()
{
// You would only need to change this method if you used the configuration-based approach.
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var attrs = assembly.GetCustomAttributes(typeof(DtoProviderAttribute), false);
foreach (DtoProviderAttribute item in attrs)
{
if (!_dtoMappings.ContainsKey(item.ProvidedType.TypeHandle))
{
var expr = Expression.Lambda<Func<object>>(Expression.Convert(Expression.New(item.ProviderType), typeof(object)));
_dtoMappings.Add(item.ProvidedType.TypeHandle, expr.Compile());
}
}
}
}
}
公共类服务工厂
{
私有静态只读字典_dtoMappings=new Dictionary();
公共静态IDocumentService GetDocumentService()
{
var rth=类型化(TDto).TypeHandle;
Func混凝土厂;
锁(包装)
{
if(_dtoMappings.TryGetValue(typeof(TDto).TypeHandle,出厂))
return(IDocumentService)concreteFactory();
FillMappings();
如果(!\u数据映射.TryGetValue(类型化(TDto).TypeHandle,出厂))
抛出新异常(“未找到具体实现”);
return(IDocumentService)concreteFactory();
}
}
私有静态void FillMappings()
{
//如果使用基于配置的方法,则只需更改此方法。
foreach(AppDomain.CurrentDomain.GetAssemblys()中的变量程序集)
{
var attrs=assembly.GetCustomAttributes(typeof(DtoProviderAttribute),false);
foreach(属性中的DtoProviderAttribute项)
{
if(!\u数据映射.ContainsKey(item.ProvidedType.TypeHandle))
{
var expr=Expression.Lambda(Expression.Convert(Expression.New(item.ProviderType),typeof(object));
_添加(item.ProvidedType.TypeHandle,expr.Compile());
}
}
}
}
}
正如“Rune”指出的:由于缓存,搜索所有程序集的开销很低:
private static void FillMappings()
{
foreach (var type in AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(x => x.IsClass && !x.IsAbstract))
{
foreach (var iface in type.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDocumentService<>)))
{
var arg = iface.GetGenericArguments()[0];
if (!_dtoMappings.ContainsKey(arg.TypeHandle))
{
var expr = Expression.Lambda<Func<object>>(Expression.Convert(Expression.New(type), typeof(object)));
_dtoMappings.Add(arg.TypeHandle, expr.Compile());
}
}
}
}
private static void FillMappings()
{
foreach(AppDomain.CurrentDomain.GetAssemblies()中的变量类型。SelectMany(x=>x.GetTypes())。其中(x=>x.IsClass&&!x.IsAbstract))
{
foreach(type.GetInterfaces()中的变量iface,其中(x=>x.IsGenericType&&x.GetGenericTypeDefinition()==typeof(IDocumentService)))
{
var arg=iface.GetGenericArguments()[0];
如果(!\u数据映射包含属性(参数类型句柄))
{
var expr=Expression.Lambda(Expression.Convert(Expression.New(type)、typeof(object));
_添加(arg.TypeHandle,expr.Compile());
}
}
}
}
下面是GetDocumentService的一个可能实现
public IDocumentService<TDto> GetDocumentService<TDto>()
{
//based on the TDto type I want to find the concrete that
//implements IDocumentService<TDto>
}
public static IDocumentService<TDto> GetDocumentService<TDto>()
{
// Gets the type for IDocumentService
Type tDto=typeof(IDocumentService<TDto>);
Type tConcrete=null;
foreach(Type t in Assembly.GetExecutingAssembly().GetTypes()){
// Find a type that implements tDto and is concrete.
// Assumes that the type is found in the executing assembly.
if(tDto.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface){
tConcrete=t;
break;
}
}
// Create an instance of the concrete type
object o=Activator.CreateInstance(tConcrete);
return (IDocumentService<TDto>)o;
}
另一种可能性:
public IDocumentService<TDto> GetDocumentService<TDto>()
{
var genericParameter = typeof(TDto);
return (from type in Assembly.GetExecutingAssembly().GetTypes() // Get Types
where type.GetConstructor(Type.EmptyTypes) != null // That is concrete
let interfaces = type.GetInterfaces()
from intf in interfaces
where intf.IsGenericType // Which implement generic interface
let genarg = intf.GetGenericArguments()[0]
where genarg == genericParameter // Where generic argument is of type genericParameter
select (IDocumentService<TDto>) // Cast to IDocumentService
Activator.CreateInstance(type)).FirstOrDefault(); // Instantiate
}
public IDocumentService GetDocumentService()
{
var genericParameter=typeof(TDto);
返回(从Assembly.getExecutionGassembly().GetTypes()//获取类型中的类型)
其中type.GetConstructor(type.EmptyTypes)!=null//这是具体的
let interfaces=type.GetInterfaces()
从接口中的intf
其中intf.IsGenericType//实现通用接口的
让genarg=intf.GetGenericArguments()[0]
其中genarg==genericParameter//其中泛型参数的类型为genericParameter
选择(IDocumentService)//转换为IDocumentService
Activator.CreateInstance(type)).FirstOrDefault();//实例化
}
搜索所有类型不需要太慢。你第一次这么做的时候,就要画一张地图。使用Dto类型作为键,使用服务作为值。下面的每个查找都是O(1)(这也是一个相当低的常数)@Rune谢谢-我想我今天有点笨。更新了答案。既然我正在使用GetCallingAssembly,我将如何进行测试?若我有一个引用我的服务程序集的单元测试程序集,那个么我将如何得到它呢?答案已编辑。老实说,我不知道这里的getexecutinggassembly
更好。哦,这非常有效,意味着要把你的答案标记出来。我从Mohamed的答案中得到了getExecutionGassembly(),并在您的代码中运行了它。你能在Mohamed Abed的回答中看到我关于取回ICommentService接口的评论吗?实际上,我想返回ICommentService,因为它实现了IDocumentService。我希望它足够通用,以便如果我传入另一个TDto类型,我将
public IDocumentService<TDto> GetDocumentService<TDto>()
{
var genericParameter = typeof(TDto);
return (from type in Assembly.GetExecutingAssembly().GetTypes() // Get Types
where type.GetConstructor(Type.EmptyTypes) != null // That is concrete
let interfaces = type.GetInterfaces()
from intf in interfaces
where intf.IsGenericType // Which implement generic interface
let genarg = intf.GetGenericArguments()[0]
where genarg == genericParameter // Where generic argument is of type genericParameter
select (IDocumentService<TDto>) // Cast to IDocumentService
Activator.CreateInstance(type)).FirstOrDefault(); // Instantiate
}