| File: Symbols\AnonymousTypes\AnonymousTypeDescriptor.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// <summary> /// Describes anonymous type in terms of fields /// </summary> internal readonly struct AnonymousTypeDescriptor : IEquatable<AnonymousTypeDescriptor> { /// <summary> Anonymous type location </summary> public readonly Location Location; /// <summary> Anonymous type fields </summary> public readonly ImmutableArray<AnonymousTypeField> Fields; /// <summary> /// Anonymous type descriptor Key /// /// The key is to be used to separate anonymous type templates in an anonymous type symbol cache. /// The type descriptors with the same keys are supposed to map to 'the same' anonymous type /// template in terms of the same generic type being used for their implementation. /// </summary> public readonly string Key; public AnonymousTypeDescriptor(ImmutableArray<AnonymousTypeField> fields, Location location) { this.Fields = fields; this.Location = location; this.Key = ComputeKey(fields, f => f.Name); } internal static string ComputeKey<T>(ImmutableArray<T> fields, Func<T, string> getName) { var key = PooledStringBuilder.GetInstance(); foreach (var field in fields) { key.Builder.Append('|'); key.Builder.Append(getName(field)); } return key.ToStringAndFree(); } [Conditional("DEBUG")] internal void AssertIsGood() { Debug.Assert(!this.Fields.IsDefault); foreach (var field in this.Fields) { field.AssertIsGood(); } } public bool Equals(AnonymousTypeDescriptor desc) { return this.Equals(desc, TypeCompareKind.ConsiderEverything); } /// <summary> /// Compares two anonymous type descriptors, takes into account fields and types, not locations. /// </summary> internal bool Equals(AnonymousTypeDescriptor other, TypeCompareKind comparison) { // Comparing keys ensures field count and field names are the same if (this.Key != other.Key) { return false; } // Compare field types return Fields.SequenceEqual( other.Fields, comparison, static (x, y, comparison) => AnonymousTypeField.Equals(x, y, comparison)); } /// <summary> /// Compares two anonymous type descriptors, takes into account fields and types, not locations. /// </summary> public override bool Equals(object? obj) { return obj is AnonymousTypeDescriptor && this.Equals((AnonymousTypeDescriptor)obj, TypeCompareKind.ConsiderEverything); } public override int GetHashCode() { return this.Key.GetHashCode(); } /// <summary> /// Creates a new anonymous type descriptor based on 'this' one, /// but having field types passed as an argument. /// </summary> internal AnonymousTypeDescriptor WithNewFieldsTypes(ImmutableArray<TypeWithAnnotations> newFieldTypes) { Debug.Assert(!newFieldTypes.IsDefault); Debug.Assert(newFieldTypes.Length == this.Fields.Length); var newFields = Fields.ZipAsArray(newFieldTypes, static (field, type) => field.WithType(type)); return new AnonymousTypeDescriptor(newFields, this.Location); } internal AnonymousTypeDescriptor SubstituteTypes(AbstractTypeMap map, out bool changed) { var oldFieldTypes = Fields.SelectAsArray(f => f.TypeWithAnnotations); var newFieldTypes = map.SubstituteTypes(oldFieldTypes); changed = (oldFieldTypes != newFieldTypes); return changed ? WithNewFieldsTypes(newFieldTypes) : this; } } }