我正在为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)
中带有声明的类型似乎是指物理代码元素。(ITypeElement, IDeclaredElement)
中带有Element的类型的含义,也许它指的是AST元素。我爱对此有所澄清。
答案 0 :(得分:9)
我在文档中添加了一个问题,以便更新:https://github.com/JetBrains/resharper-devguide/issues/4
我试着在这里提供一个盆栽的解释。
ITreeNode
类型层次结构定义代码的抽象语法树。这提供了大量信息,但是非常低级 - 它直接映射到代码的原始文本。它也错过了一些更高级别的信息。例如,如果我想获取类声明的所有类型成员,我可以为该类运行AST,并收集所有适当的树节点,但是我还必须处理部分类,并且AST提供没有关于定位班级其他部分的信息。同样,如果我看到类声明public class Foo : Bar
,我将不得不手动解析Bar
基类型。
IDeclaredElement
类型层次结构本质上是语法树的语义视图。在最简单的层面上,声明的元素是#34;具有声明的东西"。这可以是类声明,也可以是方法声明,或甚至与代码无关的东西 - HTML元素,CSS类甚至颜色和文件系统路径(这就是它被称为"元素" - 它需要一个可以应用于许多不同的事物的名称)。
例如,CLR类型用ITypeElement
接口表示,该接口派生自IDeclaredElement
。它提供了获取目标类型的方法,属性,构造函数等的声明元素的方法和属性。因此,(几乎)可以仅根据声明的方式提供CLR源项目的语义视图元素。几乎,但不完全。
声明的元素具有GetDeclarations
方法,该方法提供IDeclaration
语法树节点,这些节点是声明元素的声明。同样,IDeclaration
节点提供DeclaredElement
属性,以便能够从节点获取声明的元素。
此外,ReSharper有一个非常强大的机制叫做引用,它允许树节点有一个传出引用,它将解析为一个声明的元素(它也可能无法解析,这是一个错误,例如使用一个方法,它不是&尚未编写,或者它可以解析为多个元素,例如使用不限定哪个重载的方法)。这些引用可以应用于任何节点,例如引用变量声明的变量名称,或Bar
中public class Foo : Bar
引用Bar
的声明元素(来自哪个)可以获得IDeclaration
和Bar
的源代码。
这提供了一组令人印象深刻的功能 - 代码文件的语法视图,代码声明的语义视图以及将所有内容连接在一起的引用,但这并不涵盖所有内容。声明的元素提供声明的 thing 的语义视图,但不是用于表示所有使用场景。
具体而言(查看CLR类型),它不能将类型的用法表示为数组,指针或封闭泛型类型。 ITypeElement
可以提供类Foo
或Bar<T>
的语义视图,但它不能代表Foo[]
或Bar<Quux>
。
声明的元素需要能够将这些使用场景建模为基类,方法签名等。为此,派生的声明元素(如ITypeElement
)使用额外的接口层次结构来表示&#34;输入系统&#34;信息。此层次结构取决于所分析的语言。对于CLR类型,它是IType
层次结构,对于JavaScript,它是IJavaScriptType
。
这个IType
是附加信息,而不是声明元素的语义视图的替代。 IType
可以返回所有类型成员的符号表,但不会像ITypeElement
那样提供访问者。相反,(并且取决于被建模的内容)IType
本质上是声明元素和ISubstitution
实例的包装器,它为泛型类型参数提供替换(表示数组)作为System.Array
类型,具有底层元素类型,它本身是IType
,因为它可能是一个封闭的泛型或另一个数组)。替换也可以是空替换,它不能替代任何东西,允许表示为开放式泛型的类型,或者根本不是通用的类型。 IDeclaredType
接口是IType
,引用声明的元素。
除此之外:解析引用实际上解析为声明的元素,而
ISubstitution
再次解析为泛型。在解析方法声明签名的引用时,您需要知道它是IList<T>
以及T
是什么。
为了获得IType
实例,您需要从现有的声明元素(方法签名,基类等)中获取一个实例,或者使用TypeFactory.CreateType
创建它。如果它是通用类型,您很可能也需要指定ISubstitution
。你也可以得到一堆常见的,#34;预定义的&#34;类型,通过:
var type = psiModule.GetPredefinedType(context).String;
您可以使用这些类型传递到其中一个TypeFactory.CreateType
方法,作为您传入的ITypeElement
的类型参数。
所以,结果是,我们在源代码中声明了一个类,这给了我们ITreeNode
,IDeclaration
和ITypeDeclaration
。我们可以使用IDeclaration
或解析引用来获取此声明的语义视图IDeclaredElement
,其中ITypeElement
是表示类的派生接口。基于CLR的声明元素使用IType
来表示类型用法,例如可能需要是封闭泛型的基类,或者可能是开放泛型或数组的方法参数。 IDeclaredType
是一种类型用法,可以让我们回到声明的元素。并且类型通常在内部用声明的元素和ISubstitution
表示,它可以填充任何通用参数,或者是没有泛型参数的ID替换。最后,您可以使用IType
或使用TypeFactory.CreateType
上的属性获得PredefinedType
。
答案 1 :(得分: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);
}
}
}