Используя Roslyn, единственный механизм для определения членов документа Visual Basic выглядит следующим образом:
var members = SyntaxTree.GetRoot().DescendantNodes().Where(node =>
node is ClassStatementSyntax ||
node is FunctionAggregationSyntax ||
node is IncompleteMemberSyntax ||
node is MethodBaseSyntax ||
node is ModuleStatementSyntax ||
node is NamespaceStatementSyntax ||
node is PropertyStatementSyntax ||
node is SubNewStatementSyntax
);
Как получить член название, StarLineNumber и EndLineNumber каждого члена?
Существует не только один способ его получить:
1) Как вы попробуете: я не буду показывать этот способ для всех добрых членов (они огромны, и логика аналогична), а только для одного из них, например ClassStatementSyntax
:
ClassStatementSyntax.Identifier.ValueText
Location
как один из способов:var location = Location.Create(SyntaxTree, ClassStatementSyntax.Identifier.Span);
var startLine = location.GetLineSpan().StartLinePosition.Line;
2) Более удобный способ - использовать SemanticModel
для получения нужных вам данных:
Таким образом, вам нужно будет получать семантическую информацию только для ClassStatementSyntax
, ModuleStatementSyntxt
и NamespaceStatementSyntax
, а все их члены будут получены при вызове GetMembers()
:
...
SemanticModel semanticModel = // usually it is received from the corresponding compilation
var typeSyntax = // ClassStatementSyntax, ModuleStatementSyntxt or NamespaceStatementSyntax
string name = null;
int startLine;
int endLine;
var info = semanticModel.GetSymbolInfo(typeSyntax);
if (info.Symbol is INamespaceOrTypeSymbol typeSymbol)
{
name = typeSymbol.Name; // retrieve Name
startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line
foreach (var item in typeSymbol.GetMembers())
{
// do the same logic for retrieving name and lines for all others members without calling GetMembers()
}
}
else if (semanticModel.GetDeclaredSymbol(typeSyntax) is INamespaceOrTypeSymbol typeSymbol2)
{
name = typeSymbol2.Name; // retrieve Name
startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line
foreach (var item in typeSymbol2.GetMembers())
{
// do the same logic for retrieving name and lines for all others members without calling GetMembers()
}
}
Но обратите внимание, когда у вас есть частичное объявление, ваш DeclaringSyntaxReferences
будет иметь пару элементов, поэтому вам нужно отфильтровать SyntaxReference
по вашему текущему SyntaxTree
.
Большое спасибо Джордж. Примерно на следующий день я реализую ваше решение »
Есть идеи, почему startLine и endLine имеют одно и то же значение?
@JoginderSNahil, это означает, что у вас есть какое-то объявление, которое может располагаться полностью в одной строке, например FieldDeclarationSyntax
, PropertyDeclarationSyntax
(без Get
, Set
), MethodStatementSyntax
(для метода MustOverride
) могут быть расположены в одной строке и т. д., поэтому startLine и endLine будут одинаковыми, но startPosition и endPosition, конечно же, будут разными.
Я проанализировал только объявления пространств имен и классов, и они не находятся в одной строке. У вас работает логика?
@JoginderSNahil, приношу свои извинения, я действительно верил, что ISymbol.Locations
содержит полный набор элементов, а не только начало объявления. Я обновил ответ изменил использование Locations
на DeclaringSyntaxReferences
Ваши изменения работают отлично. Большое спасибо за такой подробный ответ.
@JoginderSNahil, это вы ошибочно проголосовали против ответа или кто-то другой? :)
Я не осознавал, что сделал это! Как я могу это исправить?
@JoginderSNahil, попробуйте проголосовать, если можете (хотите), иначе я не знаю, как отменить его, и мы не беспокоимся об этом :)
Добро пожаловать в SO. Вы можете использовать обратные кавычки ´, чтобы встроенная пометка работала как код