随着新的RC发布,我很高兴看到现在有一个属性包允许提升的诊断功能有额外的数据,在我看来,其主要用例是能够在分析仪中运行数据计算进入代码修复程序,监听该特定诊断.
我现在意识到这个属性包只允许存储字符串值.虽然这可以证明是有用的,但我仍然发现自己必须在我的分析器和我的代码修复器中运行完全相同的逻辑,因为我没有能力只保留这些信息并将其传递给它.我当然在谈论更复杂的类型,例如语法节点和符号.
例如,我创建了一个分析器,强制using在每个文件中存在特定的指令集.分析器计算缺少哪些指令,并引发诊断通知用户并以文本方式指示缺失的指令.如果我已经拥有了SyntaxNode我必须实现的(我已经在我的分析器中),代码修复提供程序将非常简单,但我现在必须在我的代码修复程序中重新运行大部分相同的逻辑(这就是为什么我最终在我的分析器中使用公共静态辅助方法提供了大量代码)
现在,这个例子自从引入属性包以来失去了一些相关性,但我仍然认为它是一个有效的用例.我特别担心分析器和代码修复器之间唯一的链接在报告的诊断位置.在我的情况下,我可以有多个DiagnosticDescriptor实例,这些实例都可以代表源自特定"规则"的不同潜在问题,由a Diagnostic和它定义Id(我不知道这是否是Roslyn代码分析领域的一个好习惯,但是似乎是一种可接受的操作方式).
底线是:对于相同的诊断ID,我可能根据具体情况在不同位置(即在完全不同的语法元素上)引发诊断.因此,我失去了将所提供的位置放在确定的和/或相关的语法元素上的"确定性",并且修复诊断的后续逻辑消失了.
那么,有没有办法将数据从分析器传递给相关的代码修复提供程序?我还考虑过转发一个源自的自定义类型的实例Diagnostic,但它看起来像是一个代码味道,而且,Diagnostic还有很多抽象成员,我需要重新实现它的唯一目的是添加一个属性,并SimpleCodeFix密封(argggghhhh)
C#6引入了在没有setter的情况下初始化属性的功能,因此现在可以使用这种语法
public class MyClass
{
public int Answer { get; } = 42;
}
Run Code Online (Sandbox Code Playgroud)
甚至这个
public class MyClass
{
public int Answer { get; }
public MyClass()
{
Answer = 42;
}
}
Run Code Online (Sandbox Code Playgroud)
我知道(或者更确切地说,强烈假设)这将被转换为readonly带有CIL中存取方法的生成字段,所以我理解这是怎么回事
public class MyClass
{
public int Answer { get; }
public MyClass CreateMyClassInstance()
{
return new MyClass()
{
Answer = 42
};
}
}
Run Code Online (Sandbox Code Playgroud)
不编译(因为赋值技术上发生在构造函数之外,这与支持readonly字段施加的限制冲突).
我的问题是为什么首先禁止这种行为?从语法和/或编译的角度来看,为什么属性赋值是属于对象初始值设定项的一部分的属性赋值,而不仅仅被视为在其后执行的额外内联逻辑,但仍然在对象的构造函数中?是设计,技术限制或向后兼容的结果,还是仅仅是一个不够重要的变化?