C# 泛型和反射接口 - 如何让智能感知发挥作用

问题描述 投票:0回答:1

我的代码(如下)正在工作......但是,目前该解决方案没有为我提供对象

rs
允许的方法的智能感知。

我如何能够将我的动态对象“转换”为接口的类型(

typeInterface
)?或者当我打电话给
as
时使用
CreateInstance

public interface IReportService<T, K>
{
    Task<GetReportDataAnswer<T>> GetReportData(GenerateReportRequest request);
    Task<GetReportAnswer<K>> GetReport(T data);
}

public class Dummy1Service : BaseReportService, IReportService<Dummy1Data, Dummy1Report>
{
    public Dummy1Service(TTAction action) : base(action)
    {
    }
    //.... implementation of the methods GetReport and GetReportData
}

...
var reflectionData = string.Format("Reports.Elements.Dummy1.Dummy1Data");
var reflectionReport = string.Format("Reports.Elements.Dummy1.Dummy1Report");
var reflectionService = string.Format("Reports.Elements.Dummy1.Dummy1Service");

Type typeData = Type.GetType(reflectionData);
Type typeReport = Type.GetType(reflectionReport);
Type typeService = Type.GetType(reflectionService);

/***************************************************************************/
//ConstructorInfo c = typeService.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

 var typeInterface = typeof(IReportService<,>).MakeGenericType(typeData, typeReport);

 dynamic rs = Activator.CreateInstance(typeService, TTAction);

 var data = await rs.GetReportData(request); //NOTE: this is the part without intellisense 
 /***************************************************************************/
c# visual-studio generics reflection
1个回答
0
投票

为了使 IntelliSense 工作,您需要为代码分析工具 (IntelliSense) 或编译器提供静态类型信息。为了简单起见,我在下面使用术语编译器。

正如您已经注意到的那样,对于

dynamic
类型来说这是不可能的,因为它没有静态类型信息。来自 https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/interop/using-type-dynamic

dynamic
类型是静态类型,但
dynamic
类型的对象会绕过静态类型检查

换句话说,它向编译器发出信号,表明您知道自己在做什么,并且编译器不必担心该对象发生了什么。所以 IntelliSense 无法帮助你。

对于使用 IntelliSense 的编译器来说,对象可以是什么。我不完全了解您的情况,但一般来说,我理解您需要构建一些

IReportService<T, K>
的实例,而不知道(在编写代码时)实例应该真正具有哪种类型。

我认为可以采取三种方法。我不认为还有更多,但我也不是什么都没有:

  1. 使用
    dynamic
    并保证编译器您知道自己在做什么。

这是目前的情况,你不喜欢它,所以它可能不是一个选择。

只有当您想要生成和使用不属于给定接口的对象时,才可以选择此选项。

那么有两种可能。调用程序集(创建/实例化接口的地方)可以在编译时访问定义类(实现接口)的程序集,也不能访问该程序集。如果有,那么您可以通过选项 2 或 3 解决这个问题。如果没有,您必须使用选项 3。

  1. 使用通用工厂方法。

来自 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics

泛型向 .NET 引入了类型参数的概念,这使得设计延迟一种或多种类型的规范的类和方法成为可能,直到由客户端代码声明和实例化类或方法为止。

如引用中所述,泛型用于推迟您想要的确切对象的规范。我们的目标是将具体类型的明确提及推迟到我们知道我们想要哪种具体类型的程度。

目标是为创建服务的方法提供类型信息,而不是通过字符串,而是通过类型参数。

IReportService<TData, TReport> CreateReportService<TService, TData, TReport>(TTAction action) where TService : IReportService<TData, TReport> { return Activator.CreateInstance(typeof(TService), action); }
这有一个缺点,即您明确声明所有类型的 

TService

 都有一个以 
TTAction
 作为参数的构造函数。此外,这还会向上级联泛型的使用,直到我们知道要使用哪一个为止。

要使用此代码,您必须像下面这样调用它

var rs = CreateReportService<Dummy1Service, Dummy1Data, Dummy1Report>(); var data = await rs.GetReportData(request);

    使用工厂和依赖注入
在某些情况下,我们可能永远不知道我们的服务是什么具体类型。在这种情况下,最好(imo)强制我们服务的调用者/客户端提供实例化其服务的方法。我们可以通过提供一个接口来创建我们接口的具体类型来做到这一点。

public interface IReportServiceFactory<TData, TReport> { // if TTAction is only specifc to Dummy1Service IReportService<TData, TReport> CreateService(); // else IReportService<TData, TReport> CreateService(TTAction action); }

注意: 如果 TTAction

 是特定于 
Dummy1Service
 并且对于服务的每个实例都是唯一的,则上述方法不起作用,必须更多地考虑如何更智能地解决此问题。

然后,对于服务的每个实现,创建一个工厂,它不是通过反射而是使用正确的构造函数来创建服务。

在主逻辑中注入工厂并调用方法来创建服务。

// Inject the factory, for example in the constructor IReportServiceFactory<TData, TReport> _factory = ...; // Use the factory var rs = _factory.CreateService(); var data = await rs.GetReportData(request);


© www.soinside.com 2019 - 2024. All rights reserved.