获取当前项目中的所有 IDeclaredType-s(对于 ReSharper Generator 插件)
Get all IDeclaredType-s in current project (for a ReSharper Generator plugin)
我正在编写一个 ReSharper 7.1 Generator 插件,需要获取当前项目中声明的所有类型的列表(classes、接口和结构 - IDeclaredType-s
)GeneratorProviderBase<CSharpGeneratorContext>.Populate
方法。
通过定期反射,它会像 Assembly.GetTypes()
一样简单,但事实证明这是一个相当大的挑战。有办法吗?
我到处搜索,文档和示例没有帮助,然后查看了每个 *Extensions
和 *Util
class,但找不到任何有用的东西...
我设法做了我需要的,但我不太确定这是否是 right/best 方法:
[GeneratorElementProvider("MyGeneratorProvider", typeof(CSharpLanguage))]
public class MyGeneratorProvider : GeneratorProviderBase<CSharpGeneratorContext>
{
public override double Priority
{
get { return 0; }
}
public override void Populate(CSharpGeneratorContext context)
{
var projectCsFiles = GetAllCSharpFilesInProject(context.PsiModule);
var declarations = projectCsFiles.SelectMany(GetDeclarationsFromCSharpFile).ToList();
context.ProvidedElements.AddRange(declarations.Select(d => new GeneratorDeclarationElement(d)));
}
private static IEnumerable<ICSharpFile> GetAllCSharpFilesInProject(IPsiModule projectModule)
{
PsiManager psiManager = projectModule.GetPsiServices().PsiManager;
return projectModule.SourceFiles.SelectMany(f => psiManager.GetPsiFiles<CSharpLanguage>(f).OfType<ICSharpFile>());
}
private static IEnumerable<ITypeDeclaration> GetDeclarationsFromCSharpFile(ICSharpFile file)
{
return file.NamespaceDeclarationNodes.SelectMany(GetDeclarationsFromCSharpNamespace);
}
private static IEnumerable<ITypeDeclaration> GetDeclarationsFromCSharpNamespace(ICSharpNamespaceDeclaration namespaceDeclaration)
{
foreach (var namespaceChild in namespaceDeclaration.Body.Children())
{
var classDeclaration = namespaceChild as IClassDeclaration;
if (classDeclaration != null)
{
yield return classDeclaration;
}
else
{
var childNamespace = namespaceChild as ICSharpNamespaceDeclaration;
if (childNamespace != null)
{
foreach (var declaration in GetDeclarationsFromCSharpNamespace(childNamespace))
{
yield return declaration;
}
}
}
}
}
}
有任何评论或更简单(甚至可能是内置)的方法吗?
这是我使用缓存得出的结论。它看起来仍然不对,但我希望它比以前的方法更好。这仍然是从 GeneratorProvider.Populate
:
中调用的
public static IEnumerable<ICSharpTypeDeclaration> GetAllPublicTypeDeclarations(this IPsiModule module)
{
var declarationCache = module.GetPsiServices().CacheManager.GetDeclarationsCache(module, false, true);
var declarations = new List<ICSharpTypeDeclaration>();
foreach (var shortName in declarationCache.GetAllShortNames())
{
var declaredElements = declarationCache.GetElementsByShortName(shortName).OfType<ITypeElement>().Where(e =>
{
var elementType = e.GetElementType();
return elementType == CLRDeclaredElementType.CLASS || elementType == CLRDeclaredElementType.INTERFACE || elementType == CLRDeclaredElementType.ENUM;
});
foreach (ITypeElement declaredElement in declaredElements)
{
var declaration = declaredElement.GetDeclarations().OfType<ICSharpTypeDeclaration>().FirstOrDefault(d => d.GetAccessRights() == AccessRights.PUBLIC);
if (declaration != null)
{
declarations.Add(declaration);
}
}
}
return declarations;
}
注意:结果不会与第一个答案相同,因为我更改了一些限制。另外,在这两种情况下,我都不关心部分 类.
我正在编写一个 ReSharper 7.1 Generator 插件,需要获取当前项目中声明的所有类型的列表(classes、接口和结构 - IDeclaredType-s
)GeneratorProviderBase<CSharpGeneratorContext>.Populate
方法。
通过定期反射,它会像 Assembly.GetTypes()
一样简单,但事实证明这是一个相当大的挑战。有办法吗?
我到处搜索,文档和示例没有帮助,然后查看了每个 *Extensions
和 *Util
class,但找不到任何有用的东西...
我设法做了我需要的,但我不太确定这是否是 right/best 方法:
[GeneratorElementProvider("MyGeneratorProvider", typeof(CSharpLanguage))]
public class MyGeneratorProvider : GeneratorProviderBase<CSharpGeneratorContext>
{
public override double Priority
{
get { return 0; }
}
public override void Populate(CSharpGeneratorContext context)
{
var projectCsFiles = GetAllCSharpFilesInProject(context.PsiModule);
var declarations = projectCsFiles.SelectMany(GetDeclarationsFromCSharpFile).ToList();
context.ProvidedElements.AddRange(declarations.Select(d => new GeneratorDeclarationElement(d)));
}
private static IEnumerable<ICSharpFile> GetAllCSharpFilesInProject(IPsiModule projectModule)
{
PsiManager psiManager = projectModule.GetPsiServices().PsiManager;
return projectModule.SourceFiles.SelectMany(f => psiManager.GetPsiFiles<CSharpLanguage>(f).OfType<ICSharpFile>());
}
private static IEnumerable<ITypeDeclaration> GetDeclarationsFromCSharpFile(ICSharpFile file)
{
return file.NamespaceDeclarationNodes.SelectMany(GetDeclarationsFromCSharpNamespace);
}
private static IEnumerable<ITypeDeclaration> GetDeclarationsFromCSharpNamespace(ICSharpNamespaceDeclaration namespaceDeclaration)
{
foreach (var namespaceChild in namespaceDeclaration.Body.Children())
{
var classDeclaration = namespaceChild as IClassDeclaration;
if (classDeclaration != null)
{
yield return classDeclaration;
}
else
{
var childNamespace = namespaceChild as ICSharpNamespaceDeclaration;
if (childNamespace != null)
{
foreach (var declaration in GetDeclarationsFromCSharpNamespace(childNamespace))
{
yield return declaration;
}
}
}
}
}
}
有任何评论或更简单(甚至可能是内置)的方法吗?
这是我使用缓存得出的结论。它看起来仍然不对,但我希望它比以前的方法更好。这仍然是从 GeneratorProvider.Populate
:
public static IEnumerable<ICSharpTypeDeclaration> GetAllPublicTypeDeclarations(this IPsiModule module)
{
var declarationCache = module.GetPsiServices().CacheManager.GetDeclarationsCache(module, false, true);
var declarations = new List<ICSharpTypeDeclaration>();
foreach (var shortName in declarationCache.GetAllShortNames())
{
var declaredElements = declarationCache.GetElementsByShortName(shortName).OfType<ITypeElement>().Where(e =>
{
var elementType = e.GetElementType();
return elementType == CLRDeclaredElementType.CLASS || elementType == CLRDeclaredElementType.INTERFACE || elementType == CLRDeclaredElementType.ENUM;
});
foreach (ITypeElement declaredElement in declaredElements)
{
var declaration = declaredElement.GetDeclarations().OfType<ICSharpTypeDeclaration>().FirstOrDefault(d => d.GetAccessRights() == AccessRights.PUBLIC);
if (declaration != null)
{
declarations.Add(declaration);
}
}
}
return declarations;
}
注意:结果不会与第一个答案相同,因为我更改了一些限制。另外,在这两种情况下,我都不关心部分 类.