使用IIncrementalGenerator
而不是ISourceGenerator
的好处之一是管道的不同阶段可以识别当前迭代的结果与前一次迭代的结果相同,并使用缓存的结果。
为了使其工作,可能任何IncrementalValueProvider
或IncrementalValuesProvider
的类型参数都需要值相等(与默认引用相等相反),这可能是为什么您看到许多使用record
实现的源生成器
这导致了一个问题,如果IncrementalValueProvider
相等函数包含一个表示相同语义对象的ISymbol
,在管道的不同迭代期间,为了缓存比较的目的,它们会被认为是相等的吗?
放弃引用ISymbol
并从中提取我需要的数据(名称,名称空间,成员等)会更好吗?
我已经深入研究了Symbol实现的Equals函数,并且不清楚,基类Symbol.Equals()
确实使用了引用比较,但这似乎在大多数(可能全部)派生类中被覆盖了。对这些重写的抽查显示它们试图具有值相等,但是再次,有很多要检查的,即使它最终使用引用相等检查,也有可能在运行之间缓存符号引用,甚至引用相等检查将为真。
放弃引用ISymbol
并从中提取我需要的数据(名称,名称空间,成员等)会更好吗?
不要在管道中包含符号。它们不仅会比较不相等,而且还会在内存中根编译,并可能导致高内存使用量。
下面的一小段代码显示了符号如何比较不相等,即使它们是由完全相同的语法树产生的:
var trees = new[] { SyntaxFactory.ParseSyntaxTree("public class C { }") };
var comp1 = CSharpCompilation.Create(null, trees);
var s1 = comp1.GetTypeByMetadataName("C");
var comp2 = CSharpCompilation.Create(null, trees);
var s2 = comp2.GetTypeByMetadataName("C");
Console.WriteLine(s1.Equals(s2, SymbolEqualityComparer.Default));
但是,即使您提供了自己的相等比较器,根编译仍然是一个问题。