创建一个泛型类型以在 Resharper 插件中查找实现

Posted

技术标签:

【中文标题】创建一个泛型类型以在 Resharper 插件中查找实现【英文标题】:Create a generic type to find implementations of in a Resharper plugin 【发布时间】:2015-06-18 14:37:46 【问题描述】:

我正在为 resharper 编写一个插件,我想用它来从 ConcreteCommand -> ConcreteCommandHandler 导航,这些类型看起来像这样

public class ConcreteCommand : ICommand

public class ConcreteCommandHandler : ICommandHandler<ConcreteCommand>

当光标位于ICommand 实例/定义上时,我已经添加了导航菜单选项(目前仅通过检查名称是否包含“Command”而不是“CommandHandler”),我想我有实际搜索继承某些东西的类型所需的代码,但我的问题是我唯一真正拥有类型的是我的ConcereteCommand,我需要创建(或获取对)泛型类型@987654326 @ 与 T 是光标当前所在的类型。

所以我还有两件事想知道:

如何检查我的IDeclaredElement 是否是特定接口的实现(最好通过在配置中的字符串中指定全名)? 如何创建ITypeElement,它是特定接口的泛型类型,我可以从现有的IDeclaredElements 类型中设置泛型类型,以便找到继承它的类?

我现有的代码如下所示:

[ContextNavigationProvider]
public class CommandHandlerNavigationProvider : INavigateFromHereProvider

    public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)
    
        ICollection<IDeclaredElement> declaredElements = dataContext.GetData(DataConstants.DECLARED_ELEMENTS);
        if (declaredElements != null || declaredElements.Any())
        
            IDeclaredElement declaredElement = declaredElements.First();

            if (IsCommand(declaredElement))
            
                var solution = dataContext.GetData(JetBrains.ProjectModel.DataContext.DataConstants.SOLUTION);
                yield return new ContextNavigation("This Command's &handler", null, NavigationActionGroup.Other, () =>  GotToInheritor(solution,declaredElement); );
            
        
    

    private void GotToInheritor(ISolution solution, IDeclaredElement declaredElement)
                
        var inheritorsConsumer = new InheritorsConsumer();
        SearchDomainFactory searchDomainFactory = solution.GetComponent<SearchDomainFactory>();
//How can I create the ITypeElement MyNameSpace.ICommandHandler<(ITypeElement)declaredElement> here?  
      solution.GetPsiServices().Finder.FindInheritors((ITypeElement)declaredElement, searchDomainFactory.CreateSearchDomain(solution, true),                    inheritorsConsumer, NullProgressIndicator.Instance);
    

    private bool IsCommand(IDeclaredElement declaredElement)
    
//How can I check if my declaredElement is an implementation of ICommand here?
        string className = declaredElement.ShortName;
        return className.Contains("Command")
               && !className.Contains("CommandHandler");
    

【问题讨论】:

【参考方案1】:

Ok 设法通过@CitizenMatt 朝着正确的方向努力解决了这个问题。

基本上我的解决方案是这样的(仍然需要整理一下)

private static readonly List<HandlerMapping> HandlerMappings = new List<HandlerMapping>

    new HandlerMapping("HandlerNavigationTest.ICommand", "HandlerNavigationTest.ICommandHandler`1", "HandlerNavigationTest"),
    new HandlerMapping("HandlerNavTest2.IEvent", "HandlerNavTest2.IEventHandler`1", "HandlerNavTest2")
;

public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)

    ICollection<IDeclaredElement> declaredElements = dataContext.GetData(DataConstants.DECLARED_ELEMENTS);
    if (declaredElements != null && declaredElements.Any())
    
        IDeclaredElement declaredElement = declaredElements.First();

        ISolution solution = dataContext.GetData(JetBrains.ProjectModel.DataContext.DataConstants.SOLUTION);
        ITypeElement handlerType = GetHandlerType(declaredElement);
        if (handlerType != null)
        
            yield return new ContextNavigation("&Handler", null, NavigationActionGroup.Other, () => GoToInheritor(solution, declaredElement as IClass, dataContext, handlerType));
        
    


private static ITypeElement GetHandlerType(IDeclaredElement declaredElement)

    var theClass = declaredElement as IClass;
    if (theClass != null)
    
        foreach (IPsiModule psiModule in declaredElement.GetPsiServices().Modules.GetModules())
        
            foreach (var handlerMapping in HandlerMappings)
            
                IDeclaredType commandInterfaceType = TypeFactory.CreateTypeByCLRName(handlerMapping.HandledType, psiModule, theClass.ResolveContext);

                ITypeElement typeElement = commandInterfaceType.GetTypeElement();
                if (typeElement != null)
                
                    if (theClass.IsDescendantOf(typeElement))
                    
                        IDeclaredType genericType = TypeFactory.CreateTypeByCLRName(handlerMapping.HandlerType, psiModule, theClass.ResolveContext);
                        ITypeElement genericTypeElement = genericType.GetTypeElement();
                        return genericTypeElement;
                    
                
            

        
    

    return null;


private static void GoToInheritor(ISolution solution, IClass theClass, IDataContext dataContext, ITypeElement genericHandlerType)

    var inheritorsConsumer = new InheritorsConsumer();
    var searchDomainFactory = solution.GetComponent<SearchDomainFactory>();

    IDeclaredType theType = TypeFactory.CreateType(theClass);
    IDeclaredType commandHandlerType = TypeFactory.CreateType(genericHandlerType, theType);
    ITypeElement handlerTypeelement = commandHandlerType.GetTypeElement();
    solution.GetPsiServices().Finder.FindInheritors(handlerTypeelement, searchDomainFactory.CreateSearchDomain(solution, true),
        inheritorsConsumer, NullProgressIndicator.Instance);
    var potentialNavigationPoints = new List<INavigationPoint>();
    foreach (ITypeElement inheritedInstance in inheritorsConsumer.FoundElements)
    
        IDeclaredType[] baseClasses = inheritedInstance.GetAllSuperTypes();
        foreach (IDeclaredType declaredType in baseClasses)
        
            if (declaredType.IsInterfaceType())
            
                if (declaredType.Equals(commandHandlerType))
                
                    var navigationPoint = new DeclaredElementNavigationPoint(inheritedInstance);
                    potentialNavigationPoints.Add(navigationPoint);
                
            
        
    

    if (potentialNavigationPoints.Any())
    
        NavigationOptions options = NavigationOptions.FromDataContext(dataContext, "Which handler do you want to navigate to?");
        NavigationManager.GetInstance(solution).Navigate(potentialNavigationPoints, options);
    
 

public class InheritorsConsumer : IFindResultConsumer<ITypeElement>

    private const int MaxInheritors = 50;

    private readonly HashSet<ITypeElement> elements = new HashSet<ITypeElement>();

    public IEnumerable<ITypeElement> FoundElements
    
        get  return elements; 
     

    public ITypeElement Build(FindResult result)
    
        var inheritedElement = result as FindResultInheritedElement;
        if (inheritedElement != null)
            return (ITypeElement) inheritedElement.DeclaredElement;
        return null;
    

    public FindExecution Merge(ITypeElement data)
    
        elements.Add(data);
        return elements.Count < MaxInheritors ? FindExecution.Continue : FindExecution.Stop;
    

这使我无法导航到多个处理程序(如果它们存在)。这目前依赖于处理类型的接口和处理程序类型在同一个程序集中。但目前这对我来说似乎足够合理。

【讨论】:

是的,你就是这样做的。理想情况下,您应该能够获得代表您的类型在其中定义的程序集的IPsiModule,如果您知道它在其中定义的项目,则通过Modules.GetPsiModules(project)Modules.GetAssemblyPsiModuleByName(assemblyName) 如果它在已编译的程序集中获得它.或者甚至只是使用Modules.GetModules() 并检查Name 属性。 谢谢@citizenmatt。不过,我一直在为第二部分苦苦挣扎。我能够以类似的方式获取泛型类型的类型元素,并且认为我知道如何设置泛型参数(使用genericType.GetSubstitution().Apply(theType);)但找不到如何从我的IDeclaredElement 获取IType当我的提供者被调用时,这是我的类。我将它转换为 IClass 和各种类型,但 Type() 扩展方法似乎总是返回 null... 再一次,你的方向是正确的。封闭的泛型类型是IDeclaredElement 的一个实例,其中ISubstitution 表示类型参数中的类型。您可以再次通过TypeFactory.CreateType 创建它,传入一个替换或IType 实例的数组。只需再次使用 TypeFactory.CreateType 从已声明的元素创建 IType @citizenmatt 哇!哇!现在在我的继承者消费者中拥有正确的类型!现在只需要让 ide 导航到此类型的代码文件。 @citizenmatt 我用我为获得正确的类/源文件所做的工作更新了我的答案,但我实际上如何让 VS 导航到这个文件?再次,我的 google-fu 似乎不够强大

以上是关于创建一个泛型类型以在 Resharper 插件中查找实现的主要内容,如果未能解决你的问题,请参考以下文章

ReSharper 警告:“泛型类型的静态字段”

Resharper 运行配置

如何在 resharper 插件中提供带有 datacontext 类型的智能感知?

如何使用 ReSharper SDK 创建 [CustomAttribute(typeof(GenericType<,>))]?

如何添加类型约束以在泛型方法中包含任何可序列化的内容?

ReSharper 插件测试中的任意检查