【问题标题】:Getting concrete type of generic method's T type获取泛型方法的 T 类型的具体类型
【发布时间】:2017-02-01 17:27:31
【问题描述】:

我确定以前有人问过这个问题,但我似乎无法找到我正在寻找的解决方案,尽管 SO/Google 搜索了很长时间,所以希望你能回答我的问题。

为简洁起见,假设我有以下对象:

public interface ISectionView { }
public class SampleSectionView : ISectionView { }
public interface ISectionService<T> where T : ISectionView { }
public class SampleSectionService : ISectionService<SampleSectionView>

现在我的控制器中有以下代码:

ISectionView sectionType = new SampleSectionView(); // *** NOTE THE TYPE
var service = _serviceResolver.Resolve(sectionType);

为此,_serviceResolver.Resolve 方法如下所示:

    public ISectionService<V> Resolve<V>(V sectionViewModel)
        where V : ISectionView 
    {
        var sectionViewModelTypeName = sectionViewModel.GetType().Name;

        // Ignore this, it's some AutoFac metadata resolution
        var resolvableService = _services.FirstOrDefault(s => s.Matches(sectionViewModelTypeName));

        if (resolvableService != null)
            return resolvableService.Value as ISectionService<V>; // Upcast, as this is the actual type we want!

        return null;
    }

所以,正如我们在上面看到的,我在控制器中创建了 sectionType 变量作为 ISectionView 类型,SampleSectionView 实现了它。这导致我在 resolve 方法中出现问题,因为它实际上在做什么(正如我在即时窗口中检查过的那样)如下:

return resolvableService.Value as ISectionService<ISectionView>;

这是一个问题,因为我的服务想要转换为以下内容:

return resolvableService.Value as ISectionService<SampleSectionView>;

我知道我已将 SampleSectionView 对象传递给此解析方法,但基于泛型 V 类型参数的转换有点丢失。

那么我怎样才能让演员识别实际的具体类型,而不是创建它的接口类型呢?

郑重声明,我知道我可以做到以下几点:

var sectionType = new SampleSectionView(); 

但这是一个问题,因为我需要一个工厂方法来创建 ISectionView,所以我知道这是我传递给 Resolve 的类型。是的,我明白为什么这是一个问题,那么可以做些什么来克服这个问题呢?如果可能的话,我宁愿有一个 switch/case 语句来处理转换。

谢谢

回答

感谢所有评论。最后,我所要做的就是将 ISectionService 接口修改为以下内容:

public interface ISectionService<out T> where T : ISectionView { }

这就够了。

【问题讨论】:

  • 你能以某种方式拥有一个非泛型接口而不是泛型接口吗?我认为您拥有as 是否正确,因为没有它它会抱怨返回类型不正确?
  • 您需要调用.Resolve&lt;SampleSectionView&gt;(...),因为编译器在查看解析调用时“知道”的唯一类型是变量的类型,它是一个接口。它不使用底层类型的类型,我理解是你想要的。
  • 克里斯,是的,resolvableService.Value 返回一个 IResolvableService。每个 ISectionService 也实现了这个接口,正是这个 IResolvableService 用于 AutoFac 目的。这就是为什么我现在需要将它与我想要的界面相协调。

标签: c# generics casting boxing


【解决方案1】:

我不确定这是否是最好的解决方案,但您可以将其包装成带有反射的非泛型方法。

class ServiceResolver
{
    public ISectionService<ISectionView> ResolveNonGeneric(ISectionView sectionViewModel)
    {
        var method = GetType()
                     .GetMethod(nameof(Resolve), BindingFlags.Public | BindingFlags.Instance)
                     .MakeGenericMethod(sectionViewModel.GetType());
        return (ISectionService<ISectionView>) method.Invoke(this, new[] { sectionViewModel });
    }

    public ISectionService<V> Resolve<V>(V sectionViewModel) where V : ISectionView 
    {
        //V is SampleSectionView 
    }
}

从而执行:

ISectionView sectionType = new SampleSectionView();
var service = _serviceResolver.ResolveNonGeneric(sectionType);

将在内部执行Resolve&lt;SampleSectionView&gt;

这需要使ISectionService&lt;T&gt; 协变。

【讨论】:

  • 谢谢,我试试这个。我正在研究访问者模式来处理一些双重调度,但这看起来要简单得多。
  • 谢谢巴希。我实现了这一点,但正如 maksymiuk 所说,关键是让 T 参数成为一个输出(协变)类型。
【解决方案2】:

接口不能以这种方式工作,唯一可以让接口IInterface&lt;IBase&gt; 转换为IInterface&lt;IDerived&gt; 的方法是使用out 参数声明接口。

我建议阅读question 中的答案。我相信它会为您提供所需的信息。

【讨论】:

  • 虽然我没有接受这个作为答案,但它确实是解决方案的一部分,所以感谢您提供相关链接。
猜你喜欢
  • 1970-01-01
  • 2011-05-11
  • 2018-11-06
  • 1970-01-01
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多