C# 如何通过泛型类型获得接口的具体实现?

C# 如何通过泛型类型获得接口的具体实现?,c#,reflection,C#,Reflection,我需要一些帮助来了解如何使用反射来获得基于Dto类型的具体实现: public interface IDocumentService<TDto> { } public interface ICommentService: IDoumentService<CommentDto> { } public abstract class DocumentService<TEntity,TDto>: IDocumentService<TDto> where

我需要一些帮助来了解如何使用反射来获得基于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
{
}
公共接口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
        }