我修改了您的代码,并使用反射和查看 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 的继承链中的任何内容都不会覆盖相等性并实现特殊的相等性检查。
在查看源代码时MethodSymbol
,它不会覆盖Object.Equals(object)
。(它确实覆盖了一个相关的方法;稍后会详细介绍。)
MethodSymbol
源自Symbol
。的来源Symbol
表明它确实 override Object.Equals(object)
,这反过来又调用了另一个 Equals 函数。注意实现和评论:
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);
}