我需要一些帮助来弄清楚如何使用反射来获得基于Dto类型的具体实现:
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
{
}
所以,我想做的是,将CommentDto传递给一个方法,这样我就可以获得CommentService。
public IDocumentService<TDto> GetDocumentService<TDto>()
{
//based on the TDto type I want to find the concrete that
//implements IDocumentService<TDto>
}
我将这样命名它:
var commentDocumentService = GetDocumentService<CommentDto>();
所以,我会得到CommentService,知道我只会看到IDocumentService接口的方法部分。
这是GetDocumentService的一个可能实现。
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;
}
不清楚是否要返回一个新对象,所以我假设是。
编辑:由于你的评论,这里是一个修改版本的GetDocumentService。缺点是需要指定另一个类型参数。不过,这种方法的优点是提供了一定程度的类型安全性,因为两个类型参数必须兼容。
public static T GetDocumentService<TDto, T>() where T : IDocumentService<TDto>
{
// Gets the type for IDocumentService
Type tDto=typeof(T);
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 calling 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 (T)o;
}
编辑2:如果我理解正确的话,您希望通过GetDocumentService的返回值的类型实现其他接口。例如,GetDocumentService<CommentDto>
返回一个类型为CommentService
的对象,该对象实现了ICommentService
接口。如果我理解正确的话,返回值应该是一个Type对象(例如,返回值可以是typeof(ICommentService)
)。一旦你有了这个类型,你应该调用这个类型的FullName
属性来获得这个类型的名字。
对GetDocumentService
的返回值使用以下方法获取该值实现的接口类型,例如typeof(ICommentService)
。
public static Type GetDocumentServiceType<TDto>(IDocumentService<TDto> obj){
Type tDto=typeof(IDocumentService<TDto>);
foreach(Type iface in obj.GetType().GetInterfaces()){
if(tDto.IsAssignableFrom(iface) && !iface.Equals(tDto)){
return iface;
}
}
return null;
}
首先,考虑到TDto
的类型,您的CommentService
类需要以某种方式被发现。你可以从当前AppDomain
的所有程序集中搜索所有加载的类型,但是这会非常慢。
所以你有以下可行的选择:
- 在程序集中使用定义
CommentService
的属性。 - 使用配置定义此信息。
- 使用MEF。
我将演示第一种方法。首先创建属性:
[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
)。
[assembly:DtoProvider(typeof(CommentDto), typeof(CommentService))]
现在您可以使用这些属性来搜索具体的实现。
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());
}
}
}
}
}
正如'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());
}
}
}
}
另一个可能性:
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
}