我是Roslyn的新手。我试图编写一个分析器来检测 private void DoPCommand(object parameter)
{
DataGrid dg = parameter as DataGrid;
if (dg != null)
foreach (var r in dg.Items)
{
DataGridRow row = dg.ItemContainerGenerator.ContainerFromItem(r) as DataGridRow;
if (row != null)
row.BindingGroup.UpdateSources();
}
}
循环中Select
的迭代时间,例如。
foreach
我正在编写一个代码修复提供程序,将这些语句转换为
foreach (TResult item in source.Select(x => x.Foo()))
{
...
}
以下是我目前为代码修复提供程序提供的代码。 (它有点长; foreach (TSource __ in source)
{
TResult item = __.Foo();
...
}
是变化的核心所在,但是我在InlineSimpleLambdaExpressionAsync
中包含了所有内容。)
RegisterCodeFixesAsync
当我在以下代码上运行代码修复时:
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var selectInvocation = (InvocationExpressionSyntax)syntaxRoot.FindNode(diagnosticSpan);
var forEach = (ForEachStatementSyntax)selectInvocation.Parent;
context.RegisterCodeFix(
CodeAction.Create(
title: Title,
createChangedDocument: ct => InlineSelectorAsync(context.Document, forEach, selectInvocation, ct),
equivalenceKey: Title),
diagnostic);
}
private static Task<Document> InlineSelectorAsync(
Document document,
ForEachStatementSyntax forEach,
InvocationExpressionSyntax selectInvocation,
CancellationToken ct)
{
var selectorExpr = selectInvocation.ArgumentList.Arguments.Single().Expression;
switch (selectorExpr.Kind())
{
case SyntaxKind.SimpleLambdaExpression:
// This will be the most common case.
return InlineSimpleLambdaExpressionAsync(
document,
forEach,
selectInvocation,
(SimpleLambdaExpressionSyntax)selectorExpr,
ct);
}
return Task.FromResult(document);
}
private static async Task<Document> InlineSimpleLambdaExpressionAsync(
Document document,
ForEachStatementSyntax forEach,
InvocationExpressionSyntax selectInvocation,
SimpleLambdaExpressionSyntax selectorExpr,
CancellationToken ct)
{
var smodel = await document.GetSemanticModelAsync(ct).ConfigureAwait(false);
// First, change the foreach to iterate directly through the source enumerable,
// and remove the Select() method call.
// NOTE: GetSimpleMemberAccessExpression() is an extension method I wrote.
var sourceExpr = selectInvocation.GetSimpleMemberAccessExpression()?.Expression;
if (sourceExpr == null)
{
return document;
}
// Figure out the element type of the source enumerable.
var sourceTypeSymbol = smodel.GetTypeInfo(sourceExpr, ct).Type;
Debug.Assert(sourceTypeSymbol != null);
// NOTE: GetElementType is an extension method I wrote.
var elementTypeSymbol = sourceTypeSymbol.GetElementType(smodel);
// Now, update the foreach. Replace the element type of the selected enumerable
// with the element type of the source. Make '__' the identifier (TODO: Improve on this).
var ident = SyntaxFactory.Identifier("__");
int position = forEach.Type.SpanStart;
var elementTypeSyntax = SyntaxFactory.IdentifierName(elementTypeSymbol.ToMinimalDisplayString(smodel, position));
var newForEach = forEach
.WithType(elementTypeSyntax)
.WithIdentifier(ident)
.WithExpression(sourceExpr);
// Now, we have to take the selector and inline it.
var selectorBody = selectorExpr.Body as ExpressionSyntax;
Debug.Assert(selectorBody != null);
var selectorParam = selectorExpr.Parameter;
selectorBody = selectorBody.ReplaceNode(selectorParam, SyntaxFactory.IdentifierName("__")); // This doesn't work.
var selectorStatement = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
type: forEach.Type,
variables: SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(
identifier: forEach.Identifier,
argumentList: null,
initializer: SyntaxFactory.EqualsValueClause(selectorBody)))));
var forEachStatment = forEach.Statement as BlockSyntax;
// TODO: Consider supporting non-block statements? Would that happen with no braces?
if (forEachStatment == null)
{
return document;
}
newForEach = newForEach.WithStatement(
// NOTE: InsertStatements is an extension method I wrote.
forEachStatment.InsertStatements(0, selectorStatement));
// Update the syntax root and the document.
var syntaxRoot = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false);
syntaxRoot = syntaxRoot.ReplaceNode(forEach, newForEach);
return document.WithSyntaxRoot(syntaxRoot);
}
我明白了:
foreach (var item in array.Select(x => x.ToString()))
{
}
除了空白之外,几乎正是我想要的,除了我无法弄清楚如何用 foreach (int __ in array)
{
var item = x.ToString();
}
替换参数x
。特别是,这条线似乎不起作用:
__
我试图将selectorBody = selectorBody.ReplaceNode(selectorParam, SyntaxFactory.IdentifierName("__"));
的{{1}}替换为Body
,SimpleLambdaExpression
无效。我怀疑这不会起作用,但我怎样才能用Parameter
替换x
lambda中的用法?
答案 0 :(得分:1)
您正在尝试替换selectorParam
中缺少的selectorBody
节点,因为在您的情况下,selectorBody是对方法的调用(x.ToString()
- InvocationExpressionSyntax
) 。您可以通过重写代码来获得替换:
var selectorBody = selectorExpr.Body as InvocationExpressionSyntax;
var nodeToReplace = (selectorBody.Expression as MemberAccessExpressionSyntax).Expression;
selectorBody = selectorBody.ReplaceNode(nodeToReplace, SyntaxFactory.IdentifierName("__"));
或者,如果您想依赖参数,那么您应该以下列方式替换SyntaxNode
,而不是SyntaxTokens
:
var selectorBody = selectorExpr.Body as ExpressionSyntax;
var selectorParam = selectorExpr.Parameter.Identifier;
IEnumerable<SyntaxToken> tokensToReplace = selectorBody.DescendantTokens()
.Where(token => String.Equals(token.Text, selectorParam.Text));
selectorBody = selectorBody.ReplaceTokens(tokensToReplace, (t1, t2) => SyntaxFactory.IdentifierName("__").Identifier);