如何使用 Resharper SDK 从 IClrDeclaredElement 获取 IDeclaredType
Posted
技术标签:
【中文标题】如何使用 Resharper SDK 从 IClrDeclaredElement 获取 IDeclaredType【英文标题】:How do I get an IDeclaredType from an IClrDeclaredElement with the Resharper SDK 【发布时间】:2014-11-07 04:51:18 【问题描述】:我正在为 Resharper 编写一个导航插件,我的情况是我有一个 IDeclaredElement
的列表,这是我从做得到的
var declaredElements = context.GetData(DataConstants.DECLARED_ELEMENTS)
此元素是用户将鼠标光标置于其上的元素。
我想做的是获取声明元素的IDeclaredType
,包括它可能具有的任何类型参数,如果它是泛型类型。
The resharper SDK documentation is quite light when it comes to the type system,并没有真正解释各种类型之间的关系。
我搜索了其他插件以尝试找到这方面的示例,但都没有找到。我检查了每个 Util 和 Extension 类,看看是否有某个方法可以满足我的需求,但没有。
我发现的唯一东西是这样的:
declaredElements.First().GetSuperTypes()
返回类型层次结构,不包括当前类型。有用,但不是我想要的。
是否有人对此 API 有任何经验或了解其工作原理?我想要一个能更多解释类型之间关系的答案。
我的理解,简单来说:
名称(IDeclaredElement, IDeclaredType)
中带有已声明 的类型似乎是指物理代码元素。
IType 似乎是所有类型的***接口,与物理代码元素不对应
我不清楚 (ITypeElement, IDeclaredElement)
名称中带有 Element 的类型的含义,可能是指 AST 元素。
我希望喜欢对此进行一些澄清。
【问题讨论】:
能否请您稍微澄清一下您的问题。就您想要实现的最终用户功能而言。我之所以问,是因为在一般情况下没有单一的 API 可以解决您的问题,但是如果有问题,我可以提供一些建议。谢谢。 【参考方案1】:我在文档中添加了一个问题以更新此问题:https://github.com/JetBrains/resharper-devguide/issues/4
我会尝试在这里提供一个固定的解释。
ITreeNode
类型层次结构定义了代码的抽象语法树。这提供了很多信息,但是非常底层——它直接映射到代码的原始文本。它还遗漏了一些更高级别的信息。例如,如果我想获取类声明的所有类型成员,我可以遍历该类的 AST,并收集所有适当的树节点,但我还必须处理部分类,而 AST 不提供任何信息用于定位班级的其他部分。同样,如果我看到类声明 public class Foo : Bar
,我将不得不手动解析 Bar
基类型。
IDeclaredElement
类型层次结构本质上是语法树的语义视图。在最简单的层面上,声明的元素是“具有声明的东西”。这可以是一个类声明,或者一个方法声明,或者甚至与代码无关的东西——html 元素、CSS 类甚至颜色和文件系统路径(这就是它被称为“元素”的原因——它需要一个可以适用于很多不同的事物)。
例如,CLR 类型用ITypeElement
接口表示,该接口派生自IDeclaredElement
。它提供了获取目标类型的方法、属性、构造函数等的声明元素的方法和属性。因此,(几乎)可以仅根据声明的元素来提供 CLR 源项目的语义视图。差不多,但不完全。
已声明元素具有GetDeclarations
方法,该方法提供IDeclaration
语法树节点,这些节点是已声明元素的声明。同样,IDeclaration
节点提供了DeclaredElement
属性,以便能够从节点中获取已声明的元素。
此外,ReSharper 有一个非常强大的机制,称为引用,它允许树节点有一个传出引用,该引用将解析为已声明的元素(它也可能无法解析,这是一个错误,例如使用的方法'尚未编写,或者它可以解析为多个元素,例如使用不限定它是哪个重载的方法)。这些引用可以应用于任何节点,例如引用变量声明的变量名,或者 public class Foo : Bar
中的 Bar
引用了 Bar
的声明元素(从中可以获得 @ 987654340@和Bar
的源代码)。
这提供了一组令人印象深刻的功能 - 代码文件的语法视图、代码声明的语义视图以及将所有内容连接在一起的引用,但这并没有涵盖所有内容。声明的元素提供声明的事物的语义视图,但并不打算代表所有使用场景。
具体来说(查看 CLR 类型),它不能将类型的用法表示为数组、指针或封闭的泛型类型。 ITypeElement
可以提供类Foo
或Bar<T>
的语义视图,但不能表示Foo[]
或Bar<Quux>
。
声明的元素需要能够将这些使用场景建模为基类、方法签名等。为此,派生的声明元素(例如ITypeElement
)使用额外的接口层次结构来表示这种“类型”系统”信息。这种层次结构取决于被分析的语言。对于 CLR 类型,它是 IType
层次结构,对于 javascript,它是 IJavaScriptType
。
这个IType
是附加信息,而不是替代声明元素的语义视图。 IType
可以返回所有类型成员的符号表,但不像ITypeElement
那样提供访问器。相反,(取决于所建模的内容)IType
本质上是一个声明元素和ISubstitution
实例的包装器,它提供了泛型类型参数的替换(数组表示为System.Array
类型,与一个底层元素类型,它本身就是一个IType
,因为它可能是一个封闭的泛型,或者另一个数组)。替换也可以是空替换,它不替换任何内容,允许表示为开放泛型的类型或根本不是泛型的类型。 IDeclaredType
接口是一个 IType
,它引用已声明的元素。
旁白:解析引用实际上解析为已声明的元素和
ISubstitution
,同样,用于建模泛型。在解析方法声明签名的引用时,您需要知道它是IList<T>
,以及T
是什么。
为了获得IType
实例,您需要从现有的已声明元素(方法签名、基类等)中获得一个,或者使用TypeFactory.CreateType
创建它。如果ISubstitution
是泛型类型,您很可能还需要指定它。您还可以通过以下方式获得一堆常见的“预定义”类型:
var type = psiModule.GetPredefinedType(context).String;
您可以使用这些类型传递给TypeFactory.CreateType
方法之一,作为您也传入的ITypeElement
的类型参数。
所以,结果是,我们在源代码中声明了一个类,这给了我们ITreeNode
、IDeclaration
和ITypeDeclaration
。我们可以使用IDeclaration
,或者解析引用来获得这个声明的语义视图IDeclaredElement
,其中ITypeElement
是代表类的派生接口。基于 CLR 的声明元素使用 IType
来表示类型用法,例如可能需要是封闭泛型的基类,或可能是开放泛型或数组的方法参数。 IDeclaredType
是一种类型用法,可以让我们回到已声明的元素。并且类型通常在内部用声明的元素和ISubstitution
表示,当没有泛型参数时,它可以填充任何泛型参数,或者是ID 替换。最后,您可以使用TypeFactory.CreateType
或使用PredefinedType
上的属性获得IType
。
【讨论】:
请将 pase 复制到文档中:D 一如既往,非常好的解释! 不完全复制粘贴,但它现在在文档中。 Intro、Syntax Trees、Declared elements、References 和 Type Systems【参考方案2】:查看此代码:
void Do(IDataContext dataContext)
foreach (var reference in dataContext.GetData(DataConstants.REFERENCES))
var resolveResultWithInfo = reference.Resolve().Result;
var typeElement = resolveResultWithInfo.DeclaredElement as ITypeElement;
if (typeElement != null)
var substitution = resolveResultWithInfo.Substitution;
var declaredType = TypeFactory.CreateType(typeElement, substitution);
【讨论】:
以上是关于如何使用 Resharper SDK 从 IClrDeclaredElement 获取 IDeclaredType的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ReSharper SDK 创建 [CustomAttribute(typeof(GenericType<,>))]?