C# 在Roslyn中,是否有一种方法可以在分析器和代码修复提供程序之间传递数据(而不是通过属性包)?

C# 在Roslyn中,是否有一种方法可以在分析器和代码修复提供程序之间传递数据(而不是通过属性包)?,c#,code-analysis,roslyn,C#,Code Analysis,Roslyn,在新的RC版本中,我很兴奋地看到,现在有了一个属性包,允许提出的诊断有额外的数据,在我看来,这个属性包的一个主要用例是能够将在分析器中计算的数据带入一个代码修复程序,以侦听特定的诊断 我现在意识到这个属性包只允许存储字符串值。虽然这可以证明是有用的,但我仍然发现我自己必须在我的分析器和代码修复程序中运行完全相同的逻辑,因为我没有能力只保留这些信息并将其传递下去。我当然是在谈论更复杂的类型,比如语法节点和符号 例如,我创建了一个分析器,它在每个文件中使用指令强制显示特定的。分析器计算缺少哪些指令,

在新的RC版本中,我很兴奋地看到,现在有了一个属性包,允许提出的诊断有额外的数据,在我看来,这个属性包的一个主要用例是能够将在分析器中计算的数据带入一个代码修复程序,以侦听特定的诊断

我现在意识到这个属性包只允许存储字符串值。虽然这可以证明是有用的,但我仍然发现我自己必须在我的分析器和代码修复程序中运行完全相同的逻辑,因为我没有能力只保留这些信息并将其传递下去。我当然是在谈论更复杂的类型,比如语法节点和符号

例如,我创建了一个分析器,它在每个文件中使用指令强制显示特定的
。分析器计算缺少哪些指令,并引发一个诊断,通知用户并以文本方式指示缺少的指令。如果我已经有了必须实现的
SyntaxNode
s(我已经在我的分析器中实现了),那么代码修复提供程序将非常简单,但是我现在必须在我的代码修复程序中重新运行大部分相同的逻辑(这就是为什么我最终将分析器中的大量代码放在公共静态助手方法中的原因)

现在,这个例子在引入属性包之后失去了一些相关性,但我仍然认为它是一个有效的用例。我特别关注的是,分析器和代码修复程序之间的唯一链接位于报告的诊断位置。在我的例子中,我可以有多个
diagnosticsdescriptor
实例,这些实例都可以表示由一个
Diagnostic
及其
Id
定义的特定“规则”产生的不同潜在问题(我不知道这是否是Roslyn代码分析领域的一个好做法,但似乎是一种可接受的操作方式)

底线是:对于同一个诊断Id,我可能会根据情况在不同的位置(即在完全不同的语法元素上)提出诊断。因此,我失去了“确定性”将所提供的位置放在确定的和/或相关的语法元素上,随后修复诊断的逻辑就不存在了


那么,有没有办法将数据从分析器传递到相关的代码修复提供程序?我也考虑过向下转换一个从
Diagnostic
派生的自定义类型的实例,但对我来说这似乎是一种代码气味,而且,
Diagnostic
充满了抽象成员,我需要为唯一的p重新实现这些成员添加一个属性的目的是密封的(argggghhh)

因为Kevin提到没有真正的方法来完成我本机尝试做的事情,因为诊断预期是可序列化的,这让我想到我可以通过序列化在某种程度上模拟我想要的 发布我提出的解决问题的解决方案。请随意批评和/或强调一些潜在问题

合成元素容器

公共类SyntaxElementContainer:字典
{
私有常量字符串分隔符=“…”;
私有静态只读字符串反序列化模式=GetFormattedRange(@“(\d+),@“(\d+));
私有静态字符串GetFormattedRange(字符串开始、字符串结束)
{
返回$“{start}{Separator}{end}”;
}
公共SyntaxElementContainer()
{
}
公共SyntaxElementContainer(ImmutableDictionary propertyBag)
:base(propertyBag)
{
}
public void Add(TKey nodeKey、SyntaxNode节点)
{
添加(nodeKey.ToString(),SerializeSpan(node?.Span));
}
公共无效添加(TKey tokenKey、SyntaxToken token)
{
添加(tokenKey.ToString(),SerializeSpan(token.Span));
}
公共无效添加(TKey triviaKey、SyntaxTrivia trivia)
{
添加(triviaKey.ToString(),SerializeSpan(trivia.Span));
}
公共文本SPAN GetTextSpanFromKey(字符串syntaxElementKey)
{
var spanAsText=此[syntaxElementKey];
返回反序列化span(spanAsText);
}
public int GetTextSpanStartFromKey(字符串syntaxElementKey)
{
var span=GetTextSpanFromKey(syntaxElementKey);
返回范围开始;
}
私有字符串序列化span(TextSpan?span)
{
var actualSpan=span==null | | span.Value.IsEmpty?默认值(TextSpan):span.Value;
返回GetFormattedRange(actualSpan.Start.ToString(),actualSpan.End.ToString());
}
私有文本span反序列化span(字符串spanAsText)
{
var match=Regex.match(spanAsText,反序列化模式);
如果(匹配成功)
{
var spanStartAsText=match.Groups[1]。捕获[0]。值;
var spanEndAsText=match.Groups[2]。捕获[0]。值;
返回TextSpan.FromBounds(int.Parse(spanStartAsText)、int.Parse(spanEndAsText));
}
返回新的TextSpan();
}   
}
PropertyBagSyntaxInterpreter

公共类属性BagSyntaxInterpreter
{
私有只读SyntaxNode\u根;
公共SyntaxElementContainer容器{get;}
受保护的PropertyBagSyntaxInterpreter(ImmutableDictionary propertyBag,SyntaxNode根)
{
_根=根;
容器=新的SyntaxElementContainer(propertyBag);
}
公共属性BagSyntaxInterpreter(诊断,SyntaxNode根)
:此(diagnostic.Properties,根目录)
{
}
公共SyntaxNode GetNode(TKey nodeKey)
{
return _root.FindNode(Container.GetTextSpanFromKey(nodeKey.ToString());
}
公共TSyntaxType GetNodeAs(TKey nodeKey),其中TSyntaxType:SyntaxNode
{
将_root.FindNode(Container.GetTextSpanFromKey(nodeKey.ToString())作为TSyntaxType返回;
}
公共语法标记GetToken(TKey t
public class SyntaxElementContainer<TKey> : Dictionary<string, string>
{
    private const string Separator = "...";
    private static readonly string DeserializationPattern = GetFormattedRange(@"(\d+)", @"(\d+)");

    private static string GetFormattedRange(string start, string end)
    {
        return $"{start}{Separator}{end}";
    }

    public SyntaxElementContainer()
    {
    }

    public SyntaxElementContainer(ImmutableDictionary<string, string> propertyBag)
        : base(propertyBag)
    {
    }

    public void Add(TKey nodeKey, SyntaxNode node)
    {
        Add(nodeKey.ToString(), SerializeSpan(node?.Span));
    }

    public void Add(TKey tokenKey, SyntaxToken token)
    {
        Add(tokenKey.ToString(), SerializeSpan(token.Span));
    }

    public void Add(TKey triviaKey, SyntaxTrivia trivia)
    {
        Add(triviaKey.ToString(), SerializeSpan(trivia.Span));
    }


    public TextSpan GetTextSpanFromKey(string syntaxElementKey)
    {
        var spanAsText = this[syntaxElementKey];
        return DeSerializeSpan(spanAsText);
    }

    public int GetTextSpanStartFromKey(string syntaxElementKey)
    {
        var span = GetTextSpanFromKey(syntaxElementKey);
        return span.Start;
    }

    private string SerializeSpan(TextSpan? span)
    {
        var actualSpan = span == null || span.Value.IsEmpty ? default(TextSpan) : span.Value; 
        return GetFormattedRange(actualSpan.Start.ToString(), actualSpan.End.ToString());
    }

    private TextSpan DeSerializeSpan(string spanAsText)
    {
        var match = Regex.Match(spanAsText, DeserializationPattern);
        if (match.Success)
        {
            var spanStartAsText = match.Groups[1].Captures[0].Value;
            var spanEndAsText = match.Groups[2].Captures[0].Value;

            return TextSpan.FromBounds(int.Parse(spanStartAsText), int.Parse(spanEndAsText));
        }

        return new TextSpan();
    }   
}
public class PropertyBagSyntaxInterpreter<TKey>
{
    private readonly SyntaxNode _root;

    public SyntaxElementContainer<TKey> Container { get; }

    protected PropertyBagSyntaxInterpreter(ImmutableDictionary<string, string> propertyBag, SyntaxNode root)
    {
        _root = root;
        Container = new SyntaxElementContainer<TKey>(propertyBag);
    }

    public PropertyBagSyntaxInterpreter(Diagnostic diagnostic, SyntaxNode root)
        : this(diagnostic.Properties, root)
    {
    }

    public SyntaxNode GetNode(TKey nodeKey)
    {
        return _root.FindNode(Container.GetTextSpanFromKey(nodeKey.ToString()));
    }

    public TSyntaxType GetNodeAs<TSyntaxType>(TKey nodeKey) where TSyntaxType : SyntaxNode
    {
        return _root.FindNode(Container.GetTextSpanFromKey(nodeKey.ToString())) as TSyntaxType;
    }


    public SyntaxToken GetToken(TKey tokenKey)
    {

        return _root.FindToken(Container.GetTextSpanStartFromKey(tokenKey.ToString()));
    }

    public SyntaxTrivia GetTrivia(TKey triviaKey)
    {
        return _root.FindTrivia(Container.GetTextSpanStartFromKey(triviaKey.ToString()));
    }
}
// In the analyzer
MethodDeclarationSyntax someMethodSyntax = ...
var container = new SyntaxElementContainer<string>
{
    {"TargetMethodKey", someMethodSyntax}
};

// In the code fixer
var bagInterpreter = new PropertyBagSyntaxInterpreter<string>(diagnostic, root);
var myMethod = bagInterpreter.GetNodeAs<MethodDeclarationSyntax>("TargetMethodKey");