Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/Components/Components/src/ChangeDetection.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using Microsoft.AspNetCore.Components.HotReload;

namespace Microsoft.AspNetCore.Components;

internal sealed class ChangeDetection
{
private static readonly ConcurrentDictionary<Type, bool> _immutableObjectTypesCache = new();

static ChangeDetection()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
{
HotReloadManager.Default.OnDeltaApplied += _immutableObjectTypesCache.Clear;
}
}

public static bool MayHaveChanged<T1, T2>(T1 oldValue, T2 newValue)
{
var oldIsNotNull = oldValue != null;
Expand All @@ -30,17 +43,18 @@ public static bool MayHaveChanged<T1, T2>(T1 oldValue, T2 newValue)
return false;
}

// The contents of this list need to trade off false negatives against computation
// time. So we don't want a huge list of types to check (or would have to move to
// a hashtable lookup, which is differently expensive). It's better not to include
// uncommon types here even if they are known to be immutable.
// This logic assumes that no new System.TypeCode enum entries have been declared since 7.0, or at least that any new ones
// represent immutable types. If System.TypeCode changes, review this logic to ensure it is still correct.
// Supported immutable types : bool, byte, sbyte, short, ushort, int, uint, long, ulong, char, double,
// string, DateTime, decimal, Guid, Enum, EventCallback, EventCallback<>.
// For performance reasons, the following immutable types are not supported: IntPtr, UIntPtr, Type.
private static bool IsKnownImmutableType(Type type)
=> Type.GetTypeCode(type) != TypeCode.Object
|| type == typeof(Guid)
|| _immutableObjectTypesCache.GetOrAdd(type, IsImmutableObjectTypeCore);

private static bool IsImmutableObjectTypeCore(Type type)
=> type == typeof(Guid)
|| type == typeof(DateOnly)
|| type == typeof(TimeOnly)
|| typeof(IEventCallback).IsAssignableFrom(type);
}
5 changes: 5 additions & 0 deletions src/Components/Components/test/RenderTreeDiffBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,9 @@ public void SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableA
// Arrange: Populate old and new with equivalent content
RenderFragment fragmentWillNotChange = builder => throw new NotImplementedException();
var dateTimeWillNotChange = DateTime.Now;
var dateOnlyWillNotChange = DateOnly.FromDateTime(dateTimeWillNotChange);
var timeOnlyWillNotChange = TimeOnly.FromDateTime(dateTimeWillNotChange);

foreach (var tree in new[] { oldTree, newTree })
{
tree.OpenComponent<CaptureSetParametersComponent>(0);
Expand All @@ -1758,6 +1761,8 @@ public void SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableA
tree.AddComponentParameter(1, "MyEnum", StringComparison.OrdinalIgnoreCase);
tree.AddComponentParameter(1, "MyEventCallback", EventCallback.Empty);
tree.AddComponentParameter(1, "MyEventCallbackOfT", EventCallback<int>.Empty);
tree.AddComponentParameter(1, "MyDateOnly", dateOnlyWillNotChange);
tree.AddComponentParameter(1, "MyTimeOnly", timeOnlyWillNotChange);
tree.CloseComponent();
}

Expand Down
Loading