| File: LockFile\LockFileTargetLibrary.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.ProjectModel\NuGet.ProjectModel.csproj (NuGet.ProjectModel) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using NuGet.Packaging.Core; using NuGet.Shared; using NuGet.Versioning; namespace NuGet.ProjectModel { public class LockFileTargetLibrary : IEquatable<LockFileTargetLibrary> { #region Property packing private readonly record struct PropertyKey(string PropertyName); private static readonly PropertyKey DependenciesKey = new(nameof(Dependencies)); private static readonly PropertyKey FrameworkAssembliesKey = new(nameof(FrameworkAssemblies)); private static readonly PropertyKey FrameworkReferencesKey = new(nameof(FrameworkReferences)); private static readonly PropertyKey RuntimeAssembliesKey = new(nameof(RuntimeAssemblies)); private static readonly PropertyKey ResourceAssembliesKey = new(nameof(ResourceAssemblies)); private static readonly PropertyKey CompileTimeAssembliesKey = new(nameof(CompileTimeAssemblies)); private static readonly PropertyKey NativeLibrariesKey = new(nameof(NativeLibraries)); private static readonly PropertyKey BuildKey = new(nameof(Build)); private static readonly PropertyKey BuildMultiTargetingKey = new(nameof(BuildMultiTargeting)); private static readonly PropertyKey ContentFilesKey = new(nameof(ContentFiles)); private static readonly PropertyKey RuntimeTargetsKey = new(nameof(RuntimeTargets)); private static readonly PropertyKey ToolsAssembliesKey = new(nameof(ToolsAssemblies)); private static readonly PropertyKey EmbedAssembliesKey = new(nameof(EmbedAssemblies)); private static readonly PropertyKey PackageTypeKey = new(nameof(PackageType)); private readonly Dictionary<PropertyKey, object> _propertyValues = new(); private bool _isFrozen; private IList<T> GetListProperty<T>(PropertyKey key) { if (!_propertyValues.TryGetValue(key, out object? value)) { if (_isFrozen) { return Array.Empty<T>(); } var list = new List<T>(); _propertyValues[key] = list; return list; } return (IList<T>)value; } private void SetListProperty<T>(PropertyKey key, IList<T> list) { System.Diagnostics.Debug.Assert(!_isFrozen, "Attempting to set a property on a frozen instance."); _propertyValues[key] = list; } public void Freeze() { _isFrozen = true; } #endregion public string? Name { get; set; } public string? Framework { get; set; } public NuGetVersion? Version { get; set; } public string? Type { get; set; } public IList<PackageDependency> Dependencies { get => GetListProperty<PackageDependency>(DependenciesKey); set => SetListProperty(DependenciesKey, value); } public IList<string> FrameworkAssemblies { get => GetListProperty<string>(FrameworkAssembliesKey); set => SetListProperty(FrameworkAssembliesKey, value); } public IList<string> FrameworkReferences { get => GetListProperty<string>(FrameworkReferencesKey); set => SetListProperty(FrameworkReferencesKey, value); } public IList<LockFileItem> RuntimeAssemblies { get => GetListProperty<LockFileItem>(RuntimeAssembliesKey); set => SetListProperty(RuntimeAssembliesKey, value); } public IList<LockFileItem> ResourceAssemblies { get => GetListProperty<LockFileItem>(ResourceAssembliesKey); set => SetListProperty(ResourceAssembliesKey, value); } public IList<LockFileItem> CompileTimeAssemblies { get => GetListProperty<LockFileItem>(CompileTimeAssembliesKey); set => SetListProperty(CompileTimeAssembliesKey, value); } public IList<LockFileItem> NativeLibraries { get => GetListProperty<LockFileItem>(NativeLibrariesKey); set => SetListProperty(NativeLibrariesKey, value); } public IList<LockFileItem> Build { get => GetListProperty<LockFileItem>(BuildKey); set => SetListProperty(BuildKey, value); } public IList<LockFileItem> BuildMultiTargeting { get => GetListProperty<LockFileItem>(BuildMultiTargetingKey); set => SetListProperty(BuildMultiTargetingKey, value); } public IList<LockFileContentFile> ContentFiles { get => GetListProperty<LockFileContentFile>(ContentFilesKey); set => SetListProperty(ContentFilesKey, value); } public IList<LockFileRuntimeTarget> RuntimeTargets { get => GetListProperty<LockFileRuntimeTarget>(RuntimeTargetsKey); set => SetListProperty(RuntimeTargetsKey, value); } public IList<LockFileItem> ToolsAssemblies { get => GetListProperty<LockFileItem>(ToolsAssembliesKey); set => SetListProperty(ToolsAssembliesKey, value); } public IList<LockFileItem> EmbedAssemblies { get => GetListProperty<LockFileItem>(EmbedAssembliesKey); set => SetListProperty(EmbedAssembliesKey, value); } // PackageType does not belong in Equals and HashCode, since it's only used for compatibility checking post restore. public IList<PackageType> PackageType { get => GetListProperty<PackageType>(PackageTypeKey); set => SetListProperty(PackageTypeKey, value); } public bool Equals(LockFileTargetLibrary? other) { if (other == null) { return false; } if (ReferenceEquals(this, other)) { return true; } return string.Equals(Name, other.Name, StringComparison.Ordinal) && VersionComparer.Default.Equals(Version!, other.Version!) && string.Equals(Type, other.Type, StringComparison.Ordinal) && string.Equals(Framework, other.Framework, StringComparison.Ordinal) && IsListOrderedEqual<PackageDependency>(DependenciesKey, static o => o.Id) && IsListOrderedEqual<string>(FrameworkAssembliesKey, static o => o, sequenceComparer: StringComparer.OrdinalIgnoreCase) && IsListOrderedEqual<string>(FrameworkReferencesKey, static o => o, sequenceComparer: StringComparer.OrdinalIgnoreCase) && IsListOrderedEqual<LockFileItem>(RuntimeAssembliesKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(ResourceAssembliesKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(CompileTimeAssembliesKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(NativeLibrariesKey, static o => o.Path) && IsListOrderedEqual<LockFileContentFile>(ContentFilesKey, static o => o.Path) && IsListOrderedEqual<LockFileRuntimeTarget>(RuntimeTargetsKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(BuildKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(BuildMultiTargetingKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(ToolsAssembliesKey, static o => o.Path) && IsListOrderedEqual<LockFileItem>(EmbedAssembliesKey, static o => o.Path); // NOTE we don't include PackageType in Equals or GetHashCode, since it's only used for compatibility checking post restore. bool IsListOrderedEqual<T>(PropertyKey key, Func<T, string> accessor, IEqualityComparer<T>? sequenceComparer = null) { _propertyValues.TryGetValue(key, out object? thisValue); other._propertyValues.TryGetValue(key, out object? thatValue); IList<T>? thisList = thisValue is IList<T> { Count: not 0 } list1 ? list1 : null; IList<T>? thatList = thatValue is IList<T> { Count: not 0 } list2 ? list2 : null; return thisList.OrderedEquals<T, string>(thatList, accessor, orderComparer: StringComparer.OrdinalIgnoreCase, sequenceComparer: sequenceComparer); } } public override bool Equals(object? obj) { return Equals(obj as LockFileTargetLibrary); } public override int GetHashCode() { var combiner = new HashCodeCombiner(); combiner.AddObject(Name); combiner.AddObject(Version); combiner.AddObject(Type); combiner.AddObject(Framework); combiner.AddUnorderedSequence(Dependencies); combiner.AddUnorderedSequence(FrameworkAssemblies, StringComparer.OrdinalIgnoreCase); combiner.AddUnorderedSequence(FrameworkReferences, StringComparer.OrdinalIgnoreCase); combiner.AddUnorderedSequence(RuntimeAssemblies); combiner.AddUnorderedSequence(ResourceAssemblies); combiner.AddUnorderedSequence(CompileTimeAssemblies); combiner.AddUnorderedSequence(NativeLibraries); combiner.AddUnorderedSequence(ContentFiles); combiner.AddUnorderedSequence(RuntimeTargets); combiner.AddUnorderedSequence(Build); combiner.AddUnorderedSequence(BuildMultiTargeting); combiner.AddUnorderedSequence(ToolsAssemblies); combiner.AddUnorderedSequence(EmbedAssemblies); return combiner.CombinedHash; // NOTE we don't include PackageType in Equals or GetHashCode, since it's only used for compatibility checking post restore. } } }