#c# #.net-core #roslyn
#c# #.net-ядро #roslyn
Вопрос:
Я пытаюсь получить все типы по некоторым критериям из компиляции Roslyn, созданной этим кодом:
var syntaxTrees = new[]
{
CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview))
};
// Add some references
references.Add(MetadataReference.CreateFromFile(...));
// Create compilation
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create(nameof(GeneratorRunner), syntaxTrees, references, options);
return compilation;
Затем я использую этот код для поиска типов
compilation.GetTypeByMetadataName("HomeCenter.Messages.Commands.Device.AdjustPowerLevelCommand");
compilation.GetSymbolsWithName(x => true, SymbolFilter.Type).ToList();
Для меня это работает странно — GetSymbolsWithName возвращает только типы, определенные в моих источниках, но не в ссылочных сборках, но GetTypeByMetadataName может возвращать информацию о типе, даже если он определен в ссылочной сборке. Проблема в том, что я пытаюсь искать все типы в компиляции, поэтому я не знаю точных имен. Вопрос в том, как я могу выполнять поиск по всем типам, которые приложение имеет в исходных текстах, а также в ссылочных сборках? Также есть ли какой-либо другой вариант фильтрации напротив name — меня интересуют все типы, которые наследуются от определенного типа, чтобы их можно было называть любыми способами.
Ответ №1:
Если вы хотите начать поиск по всем типам, вы можете выполнить компиляцию.GlobalNamespace, который дает вам символ INamespaceSymbol, представляющий «корневое» пространство имен иерархии. Оттуда вы можете вызвать GetTypeMembers(), чтобы получить типы, которые находятся в этом пространстве имен, и GetNamespaceMembers(), чтобы получить дочерние пространства имен. Вам придется выполнить рекурсию самостоятельно, я не думаю, что у нас есть помощник для этого.
Комментарии:
1. Хорошо, с этим я могу найти всю информацию, которую я хочу. Спасибо,
Ответ №2:
Просто дополнение к ответу от Джейсона Малиновски. Вы можете использовать следующий символ помощника посетителя:
internal class ExportedTypesCollector : SymbolVisitor
{
private readonly CancellationToken _cancellationToken;
private readonly HashSet<INamedTypeSymbol> _exportedTypes;
public ExportedTypesCollector(CancellationToken cancellation, int? estimatedCapacity = null)
{
_cancellationToken = cancellation;
_exportedTypes = estimatedCapacity.HasValue
? new HashSet<INamedTypeSymbol>(estimatedCapacity.Value, SymbolEqualityComparer.Default)
: new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
}
public ImmutableArray<INamedTypeSymbol> GetPublicTypes() => _exportedTypes.ToImmutableArray();
public override void VisitAssembly(IAssemblySymbol symbol)
{
_cancellationToken.ThrowIfCancellationRequested();
symbol.GlobalNamespace.Accept(this);
}
public override void VisitNamespace(INamespaceSymbol symbol)
{
foreach (INamespaceOrTypeSymbol namespaceOrType in symbol.GetMembers())
{
_cancellationToken.ThrowIfCancellationRequested();
namespaceOrType.Accept(this);
}
}
public override void VisitNamedType(INamedTypeSymbol type)
{
_cancellationToken.ThrowIfCancellationRequested();
if (!type.IsAccessibleOutsideOfAssembly() || !_exportedTypes.Add(type))
return;
var nestedTypes = type.GetTypeMembers();
if (nestedTypes.IsDefaultOrEmpty)
return;
foreach (INamedTypeSymbol nestedType in nestedTypes)
{
_cancellationToken.ThrowIfCancellationRequested();
nestedType.Accept(this);
}
}
}
с помощью метода расширения IsAccessibleOutsideOfAssembly
, подобного этому:
public static bool IsAccessibleOutsideOfAssembly(this ISymbol symbol) =>
symbol.DeclaredAccessibility switch
{
Accessibility.Private => false,
Accessibility.Internal => false,
Accessibility.ProtectedAndInternal => false,
Accessibility.Protected => true,
Accessibility.ProtectedOrInternal => true,
Accessibility.Public => true,
_ => true, //Here should be some reasonable default
};
Комментарии:
1. Этот посетитель великолепен, но я не понимаю одной вещи. В методе VisitNamedType есть тип. GetTypeMembers() . Когда type является классом, он действует странно для меня — он возвращает все методы точно так же, как типы с TypeKind = class ? Я думаю, что GetTypeMembers() вернет вложенные классы, но это разные?
2. Я проверил, и он возвращает вложенные типы для меня. Вы описали действительно странное поведение. Он должен возвращать вложенные типы, и я никогда не видел, чтобы он работал по-другому с типом. Однако, может быть, у вас есть объявления делегатов в вашем коде? Типы делегатов также являются типами и также будут возвращены.
3. Когда вы запускаете этот тест github.com/dominikjeske/HomeCenter/blob/MotionService / … и нажмите точку останова здесь github.com/dominikjeske/HomeCenter/blob/MotionService/… у вас есть, например, HomeCenter. Генераторы источников. Тесты. MessageGenerator . <PublishEvent>d__0 это тип класса. Но это не единственный.
4. Извините, но я еще не установил .net 5 и не работал с ним. Я не могу создать ветку MotionService в вашем репозитории и отладить тест. Изменение зависимостей на .net core 3.1 не сработало. Я не буду решать проблемы с зависимостями, потому что это займет много моего времени. Но если вы можете создать ветку, которая содержит код с проблемой и не зависит от .net 5, тогда я могу попытаться ее отладить. Или вы можете попробовать выполнить отладку в Roslyn самостоятельно с помощью Visual Studio 2019, которая поддерживает декомпиляцию и отладку в декомпилированных исходных текстах
5. Я сталкиваюсь с той же проблемой — GetTypeMembers возвращает внутренние методы.