public int GetHashCode(Symbol member) { int hash = 1; if ((object)member != null) { hash = Hash.Combine((int)member.Kind, hash); if (_considerName) { hash = Hash.Combine(ExplicitInterfaceHelpers.GetMemberNameWithoutInterfaceName(member.Name), hash); // CONSIDER: could use interface type, but that might be quite expensive } if (_considerReturnType && member.GetMemberArity() == 0 && (_typeComparison & TypeCompareKind.AllIgnoreOptions) == 0) // If it is generic, then type argument might be in return type. { hash = Hash.Combine(member.GetTypeOrReturnType().GetHashCode(), hash); } // CONSIDER: modify hash for constraints? hash = Hash.Combine(member.GetMemberArity(), hash); hash = Hash.Combine(member.GetParameterCount(), hash); } return(hash); }
internal static string GetMemberCallerName(this Symbol member) { if (member.Kind == SymbolKind.Method) { member = ((MethodSymbol)member).AssociatedSymbol ?? member; } return(member.IsIndexer() ? member.MetadataName : member.IsExplicitInterfaceImplementation() ? ExplicitInterfaceHelpers.GetMemberNameWithoutInterfaceName(member.Name) : member.Name); }
public bool Equals(Symbol member1, Symbol member2) { if (ReferenceEquals(member1, member2)) { return(true); } if ((object)member1 == null || (object)member2 == null || member1.Kind != member2.Kind) { return(false); } bool sawInterfaceInName1 = false; bool sawInterfaceInName2 = false; if (_considerName) { string name1 = ExplicitInterfaceHelpers.GetMemberNameWithoutInterfaceName(member1.Name); string name2 = ExplicitInterfaceHelpers.GetMemberNameWithoutInterfaceName(member2.Name); sawInterfaceInName1 = name1 != member1.Name; sawInterfaceInName2 = name2 != member2.Name; if (name1 != name2) { return(false); } } // NB: up to, and including, this check, we have not actually forced the (type) parameters // to be expanded - we're only using the counts. int arity = member1.GetMemberArity(); if ((arity != member2.GetMemberArity()) || (member1.GetParameterCount() != member2.GetParameterCount())) { return(false); } TypeMap typeMap1; TypeMap typeMap2; if (arity > 0 && _useSpecialHandlingForNullableTypes) { // We need this special handling in order to avoid forcing resolution of nullable types // in signature of an overriding member while we are looking for a matching overridden member. // Doing the resolution in the original signature can send us into an infinite cycle because // constraints must be inherited from the member we are looking for. // It is important to ensure that the fact whether an indexed type parameter we are about to use // is a reference type is inherited from the corresponding type parameter of the possibly overridden // member (which is member2 when _useSpecialHandlingForNullableTypes is true). This will ensure // proper resolution for nullable types in substituted signature of member1, ensuring proper // comparison of types across both members. ArrayBuilder <TypeParameterSymbol> builder = ArrayBuilder <TypeParameterSymbol> .GetInstance(arity); var typeParameters2 = member2.GetMemberTypeParameters(); for (int i = arity - 1; i >= 0; i--) { builder.Add(IndexedTypeParameterSymbolForOverriding.GetTypeParameter(i, typeParameters2[i].IsValueType)); } var indexed = builder.ToImmutableAndFree(); typeMap1 = new TypeMap(member1.GetMemberTypeParameters(), indexed, true); typeMap2 = new TypeMap(typeParameters2, indexed, true); } else { typeMap1 = GetTypeMap(member1); typeMap2 = GetTypeMap(member2); } if ((_considerReturnRefKindDifferences || _considerReturnType) && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _typeComparison)) { return(false); } if (member1.GetParameterCount() > 0 && !HaveSameParameterTypes(member1.GetParameters(), typeMap1, member2.GetParameters(), typeMap2, _considerRefKindDifferences, _typeComparison)) { return(false); } if (_considerCallingConvention) { if (GetCallingConvention(member1) != GetCallingConvention(member2)) { return(false); } } else { if (IsVarargMethod(member1) != IsVarargMethod(member2)) { return(false); } } if (_considerExplicitlyImplementedInterfaces) { if (sawInterfaceInName1 != sawInterfaceInName2) { return(false); } // The purpose of this check is to determine whether the interface parts of the member names agree, // but to do so using robust symbolic checks, rather than syntactic ones. Therefore, if neither member // name contains an interface name, this check is not relevant. // Phrased differently, the explicitly implemented interface is not part of the signature unless it's // part of the name. if (sawInterfaceInName1) { Debug.Assert(sawInterfaceInName2); // May avoid realizing interface members. if (member1.IsExplicitInterfaceImplementation() != member2.IsExplicitInterfaceImplementation()) { return(false); } // By comparing symbols, rather than syntax, we gain the flexibility of ignoring whitespace // and gracefully accepting multiple names for the same (or equivalent) types (e.g. "I<int>.M" // vs "I<System.Int32>.M"), but we lose the connection with the name. For example, in metadata, // a method name "I.M" could have nothing to do with "I" but explicitly implement interface "I2". // We will behave as if the method was really named "I2.M". Furthermore, in metadata, a method // can explicitly implement more than one interface method, in which case it doesn't really // make sense to pretend that all of them are part of the signature. var explicitInterfaceImplementations1 = member1.GetExplicitInterfaceImplementations(); var explicitInterfaceImplementations2 = member2.GetExplicitInterfaceImplementations(); if (!explicitInterfaceImplementations1.SetEquals(explicitInterfaceImplementations2, EqualityComparer <Symbol> .Default)) { return(false); } } } return(!_considerTypeConstraints || HaveSameConstraints(member1, typeMap1, member2, typeMap2)); }