我修改了您的代码,并结合使用反射并查看 Roslyn 源代码,发现 usedSymbol 和 declaredSymbol 最终成为两种不同的符号类型。
var tree = CSharpSyntaxTree.ParseText(@"
bool a = 3 > 5;
namespace System{
public struct Int32
{
public static extern bool operator > (int a, int b);
}
public struct Boolean { }
}");
var compilation = CSharpCompilation.Create("bla").AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
var usedSymbol = model.GetSymbolInfo(tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().Single()).Symbol;
var declaredSymbol = model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<OperatorDeclarationSyntax>().Single());
Type used = usedSymbol.GetType();
Type declared = declaredSymbol.GetType();
var usedUnderlying = used.GetField("_underlying", BindingFlags.NonPublic | BindingFlags.Instance);
var usedUnderlyingValue = usedUnderlying.GetValue(usedSymbol);
var declaredUnderlying = declared.GetField("_underlying", BindingFlags.NonPublic | BindingFlags.Instance);
var declaredUnderlyingValue = declaredUnderlying.GetValue(declaredSymbol);
Type usedSymbolType = usedUnderlyingValue.GetType(); //SynthesizedIntrinsicOperatorSymbol
Type declaredSymbolType = declaredUnderlyingValue.GetType(); //SourceUserDefinedOperatorSymbol
Console.WriteLine(usedSymbolType.ToString());
Console.WriteLine(declaredSymbolType.ToString());
Console.WriteLine(
$"{declaredSymbol} and {usedSymbol} are {(declaredSymbol.Equals(usedSymbol) ? "" : "not ")}equal.");
符号的两种表示的类型不匹配。一种是 SynthesizedIntrinsicOperatorSymbol,另一种是 SourceUserDefinedOperatorSymbol。归根结底,这就是为什么平等不起作用的原因——这两种类型似乎都没有实现。
例如,SynthesizedIntrinsicOperatorSymbol 的相等性会进行类型检查,在此用例中会失败:
public override bool Equals(Symbol obj, TypeCompareKind compareKind)
{
if (obj == (object)this)
{
return true;
}
var other = obj as SynthesizedIntrinsicOperatorSymbol;
if ((object)other == null)
{
return false;
}
if (_isCheckedBuiltin == other._isCheckedBuiltin &&
_parameters.Length == other._parameters.Length &&
string.Equals(_name, other._name, StringComparison.Ordinal) &&
TypeSymbol.Equals(_containingType, other._containingType, compareKind) &&
TypeSymbol.Equals(_returnType, other._returnType, compareKind))
{
for (int i = 0; i < _parameters.Length; i++)
{
if (!TypeSymbol.Equals(_parameters[i].Type, other._parameters[i].Type, compareKind))
{
return false;
}
}
return true;
}
return false;
}
查看另一种类型,SourceUserDefinedOperatorSymbol,发现相等是在许多层深的基类上实现的:Symbols.MethodSymbol。 SourceUserDefinedOperatorSymbol 的继承链中没有任何内容会覆盖相等性并实现特殊的相等性检查。
在查看source for MethodSymbol 时,它不会覆盖Object.Equals(object)。 (它确实覆盖了一个相关的方法;稍后会详细介绍。)
MethodSymbol 派生自 Symbol。 source of Symbol 表明它确实覆盖了 Object.Equals(object),而 Object.Equals(object) 又调用了另一个 Equals 函数。注意实现和 cmets:
public sealed override bool Equals(object obj)
{
return this.Equals(obj as Symbol, SymbolEqualityComparer.Default.CompareKind);
}
// By default we don't consider the compareKind, and do reference equality. This can be overridden.
public virtual bool Equals(Symbol other, TypeCompareKind compareKind)
{
return (object)this == other;
}
所以看来这个类只是返回引用相等设计。
Equals(Symbol, TypeCompareKind) 方法是virtual,MethodSymbol 类覆盖它,但仅用于检查特定类型。因为这种类型 (SourceUserDefinedOperatorSymbol) 的继承链中没有任何内容会覆盖相等方法,所以您的代码最终仍会调用使用引用相等的 base 版本:
public override bool Equals(Symbol other, TypeCompareKind compareKind)
{
if (other is SubstitutedMethodSymbol sms)
{
return sms.Equals(this, compareKind);
}
if (other is NativeIntegerMethodSymbol nms)
{
return nms.Equals(this, compareKind);
}
return base.Equals(other, compareKind);
}