如何扫描通用接口?

huangapple go评论50阅读模式
英文:

How can I scan for generic interfaces?

问题

以下是代码的中文翻译部分:

我正在管理不同类型的产品,并从不同的制造商API中获取它们的信息。它们共享一些公共属性,因此我决定将类型定义为泛型:

public class Product<T> where T : class, new() {
  public string Name { get; set; } = string.Empty;
  public T ProductInfo { get; set; } = new T();
}

我的应用程序将获取一个产品DTO列表,我需要从不同地方检索其详细信息。我决定创建一个每个ProductRetriever都将实现的接口。我将询问每个ProductRetriever是否能够检索信息。如果可以,我将调用其Retrieve方法。

由于每个ProductRetriever将返回Product<T>的特定ProductInfo,我决定将其定义为泛型接口:

public interface IProductRetriever<TProductInfo> where TProductInfo : class, new() {
  Task<bool> CanRetrieve(ProductCategoryEnum productCategory);
  Task<Product<TProductInfo>?> Retrieve(ProductDto dto);
}

我已经实现了几个ProductRetriever,我手动一个接一个地调用它们,它们工作正常。例如:

var manufacturerOneRetriever = new ManufacturerOneRetriever<Product1>();
if (manufacturerOneRetriever.CanRetrieve(ProductCategoryEnum.Automotive)) {
   var product = manufacturerOneRetriever.Retrieve(productDto);
}

现在,我想要扫描程序集以查找实现IProductRetriever<TProductInfo>接口的类,并调用它们。

在我有泛型接口之前,我能够扫描程序集并向容器注册ProductRetrievers

using IHost host = Host.CreateDefaultBuilder(args)
  .ConfigureServices(services => {
    var iProductRetrievers = typeof(IProductRetriever).Assembly.GetTypes()
      .Where(x => x is { IsAbstract: false, IsClass: true } && x.GetInterface(nameof(IProductRetriever)) == typeof(IProductRetriever));

    foreach (var rule in iProductRetrievers) {
      services.Add(new ServiceDescriptor(typeof(IProductRetriever), rule, ServiceLifetime.Transient));
    }
  })
  .Build();

现在,我将其定义为泛型接口后,在以下部分的泛型部分x.GetInterface(nameof(IProductRetriever<>))中出现错误:

.Where(x => x is { IsAbstract: false, IsClass: true } && x.GetInterface(nameof(IProductRetriever<>)) == typeof(IProductRetriever<>))
英文:

I am managing different types of products and I am getting their information from different manufacturers' APIs. They share some common properties, so I decided to make the type be generic:

public class Product&lt;T&gt; where T : class, new() {
  public string Name { get; set; } = string.Empty;
  public T ProductInfo { get; set; } = new T();
}

My application will be getting a list of productDTOs whose details I will need to retrieve from different places. I decided to create an interface that each ProductRetriever will implement. I will ask each ProductRetriever if they can retrieve the information. If it can, I will call its Retrieve method.

Since each ProductRetriever will be returning the specific ProductInfo for the Product&lt;T&gt;, I decided to make it a generic interface:

public interface IProductRetriever&lt;TProductInfo&gt; where T : class, new() {
  Task&lt;bool&gt; CanRetrieve(ProductCategoryEnum productCategory);
  Task&lt;Product&lt;TProductInfo&gt;?&gt; Retrieve(ProductDto dto);
}

I've implemented several ProductRetrievers which I've manually called one at a time and they work fine. For example:

var manufacturerOneRetriever = new ManufacturerOneRetriever&lt;Product1&gt;();
if(manufacturerOneRetriever.CanRetrieve(ProductCategoryEnum.Automotive)) {
   var product = manufacturerOneRetriever.Retrieve(productDto);
}

Now, I would like to scan the assembly searching for the classes that implement the IProductRetriever&lt;TProductInfo&gt; and call them.

Before I had the generic interface, I was able to scan the assembly and register the ProductRetrievers with the container:

using IHost host = Host.CreateDefaultBuilder(args)
  .ConfigureServices(services =&gt; {
    var iProductRetrievers = typeof(IProductRetriever).Assembly.GetTypes()
      .Where(x =&gt; x is { IsAbstract: false, IsClass: true } &amp;&amp; x.GetInterface(nameof(IProductRetriever)) == typeof(IProductRetriever));

    foreach (var rule in iProductRetrievers) {
      services.Add(new ServiceDescriptor(typeof(IProductRetriever), rule, ServiceLifetime.Transient));
    }
  })
  .Build();

Now that I have it as a generic interface, I am getting an error in the generic portion of x.GetInterface(nameof(IProductRetriever&lt;&gt;)) in:

.Where(x =&gt; x is { IsAbstract: false, IsClass: true } &amp;&amp; x.GetInterface(nameof(IProductRetriever&lt;&gt;)) == typeof(IProductRetriever&lt;&gt;))

答案1

得分: 3

GetInterface 的文档中:

> 注意
>
> 对于泛型接口,名称参数是已编码的名称,以重音符号(<code>`</code>)结尾,后跟类型参数的数量。这对泛型接口定义和构造的泛型接口都适用。例如,要查找 IExample&lt;T&gt;IExample&lt;string&gt;,请搜索 <code>"IExample`1"</code>。

typeof 不同,nameof 不能用于开放泛型类型,例如 IProductRetriever&lt;&gt;。即使您传入一个构造类型,它也只会求值为泛型类型的简单名称,而不包括<code>`1</code>部分。

您可以使用字符串文字,或者使用 typeof(IProductRetriever&lt;&gt;).Name

还要注意,GetInterface 返回一个构造类型,其中填充了类型参数,因此您不能直接将其与 typeof(IProductRetriever&lt;&gt;) 进行比较。您应该首先获取其泛型类型定义:

x.GetInterface("IProductRetriever`1").GetGenericTypeDefinition() == typeof(IProductRetriever)

我不确定 ServiceDescriptor 如何工作,但如果它也需要构造的泛型类型,您需要再次使用 GetInterface 的返回值。您将传入:

rule.GetType().GetInterface("IProductRetriever`1")
英文:

From the documentation of GetInterface

> Note
>
> For generic interfaces, the name parameter is the mangled name, ending
> with a grave accent (<code>`</code>) and the number of type
> parameters. This is true for both generic interface definitions and
> constructed generic interfaces. For example, to find IExample&lt;T&gt; or
> IExample&lt;string&gt;, search for <code>"IExample`1"</code>.

Unlike typeof, nameof cannot be used with an open generic type like IProductRetriever&lt;&gt;. Even if you pass in a constructed type, it will only evaluate to the simple name of the generic type, without the <code>`1</code> part.

You can either use a string literal, or typeof(IProductRetriever&lt;&gt;).Name instead.

Also note that GetInterface returns a constructed type, with the type parameters filled in, so you can't directly compare it to typeof(IProductRetriever&lt;&gt;). You should get its generic type definition first:

x.GetInterface(&quot;IProductRetriever`1&quot;).GetGenericTypeDefinition() == typeof(IProductRetriever)

I'm not sure how ServiceDescriptor works, but if it needs a constructed generic type too, you would need to use the return value of GetInterface again. You would pass in

rule.GetType().GetInterface(&quot;IProductRetriever`1&quot;)

huangapple
  • 本文由 发表于 2023年6月15日 06:08:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477883.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定