Skip to content

Refactor WeakReferenceList/CopyOnWriteList as type-safe generic collections #10889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7555a65
Reformat to startup
h3xds1nz May 26, 2025
0c2f1ce
Replace ArrayList with List<T> / ReadOnlyCollection<T>
h3xds1nz May 26, 2025
1a991d2
Seal WeakReferenceList, clean up some checks
h3xds1nz May 26, 2025
d4d4230
Nullable context on WeakReferenceEnumerator, make properties readonly
h3xds1nz May 26, 2025
c4c3136
Remove "base" references
h3xds1nz May 26, 2025
f070564
Fix formatting on WeakReferenceList
h3xds1nz May 26, 2025
00984e8
more syntax changes in CopyOnWriteList
h3xds1nz May 26, 2025
2973bff
File-scoped namespace in CopyOnWriteList
h3xds1nz May 26, 2025
9c98e8f
Convert WeakReferenceList/Enumerator as generic
h3xds1nz May 26, 2025
e3286ae
Use the perks of generic collections, avoid null-checks that are alwa…
h3xds1nz May 26, 2025
80927a4
Nullify reference in Dispose for enumerator
h3xds1nz May 26, 2025
87be70e
nullability on CopyOnWriteList
h3xds1nz May 26, 2025
4ef74f5
Nullability on WeakReferenceList
h3xds1nz May 26, 2025
d3dc70b
Nullability on WeakReferenceList
h3xds1nz May 26, 2025
75a53a0
Support capacity on WeakReferenceList
h3xds1nz May 26, 2025
7255907
Simplify some checks
h3xds1nz May 26, 2025
e21e6c2
Copy list with pre-allocation manually
h3xds1nz May 27, 2025
2df7f61
Remove casts from NotifyOwners dictionary
h3xds1nz May 27, 2025
29fca9e
Clean up casts in RemoveParentOwners
h3xds1nz May 27, 2025
eb2653c
Avoid type checks during Contains
h3xds1nz May 27, 2025
7575d11
Fix up some documentation
h3xds1nz May 27, 2025
9294376
Fix build
h3xds1nz May 27, 2025
1d5a7e0
Avoid casts during enumeration
h3xds1nz May 27, 2025
3b36966
Fix naming and simplify checks
h3xds1nz May 27, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ protected void RootChanged(Visual oldRoot, Visual newRoot)
// To fire PresentationSourceChanged when the RootVisual changes;
// rather than simulate a "parent" pointer change, we just walk the
// collection of all nodes that need the event.
foreach (DependencyObject element in _watchers)
foreach (DependencyObject element in s_watchers)
{
// We only need to update those elements that are in the
// same context as this presentation source.
Expand All @@ -475,15 +475,15 @@ protected void RootChanged(Visual oldRoot, Visual newRoot)
/// </summary>
protected void AddSource()
{
_sources.Add(this);
s_sources.Add(this);
}

/// <summary>
/// Called by derived classes to indicate that they no longer need to be tracked.
/// </summary>
protected void RemoveSource()
{
_sources.Remove(this);
s_sources.Remove(this);
}

/// <summary>
Expand Down Expand Up @@ -613,17 +613,14 @@ internal static bool IsUnderSamePresentationSource(params ReadOnlySpan<Dependenc
/// over a ReadOnly SnapShot of the List of sources. The Enumerator
/// skips over the any dead weak references in the list.
/// </summary>
internal static WeakReferenceList CriticalCurrentSources
internal static WeakReferenceList<PresentationSource> CriticalCurrentSources
{
get
{
return _sources;
}
get => s_sources;
}

private static void AddElementToWatchList(DependencyObject element)
{
if(_watchers.Add(element))
if (s_watchers.Add(element))
{
element.SetValue(CachedSourceProperty, PresentationSource.FindSource(element));
element.SetValue(GetsSourceChangedEventProperty, true);
Expand All @@ -633,7 +630,7 @@ private static void AddElementToWatchList(DependencyObject element)

private static void RemoveElementFromWatchList(DependencyObject element)
{
if(_watchers.Remove(element))
if (s_watchers.Remove(element))
{
element.ClearValue(CachedSourceProperty);
element.ClearValue(GetsSourceChangedEventProperty);
Expand Down Expand Up @@ -741,11 +738,11 @@ private static readonly DependencyProperty CachedSourceProperty
private static readonly object _globalLock = new object();

// An array of weak-references to sources that we know about.
private static WeakReferenceList _sources = new WeakReferenceList(_globalLock);
private static readonly WeakReferenceList<PresentationSource> s_sources = new(_globalLock);

// An array of weak-references to elements that need to know
// about source changes.
private static WeakReferenceList _watchers = new WeakReferenceList(_globalLock);
private static readonly WeakReferenceList<DependencyObject> s_watchers = new(_globalLock);

#endregion
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

//
Expand Down Expand Up @@ -51,7 +51,7 @@ public static IEnumerable<ResourceDictionaryInfo> ThemedResourceDictionaries
{
if (!IsEnabled)
{
return ResourceDictionaryDiagnostics.EmptyResourceDictionaryInfos;
return ReadOnlyCollection<ResourceDictionaryInfo>.Empty;
}

return SystemResources.ThemedResourceDictionaries;
Expand All @@ -69,7 +69,7 @@ public static IEnumerable<ResourceDictionaryInfo> GenericResourceDictionaries
{
if (!IsEnabled)
{
return ResourceDictionaryDiagnostics.EmptyResourceDictionaryInfos;
return ReadOnlyCollection<ResourceDictionaryInfo>.Empty;
}

return SystemResources.GenericResourceDictionaries;
Expand Down Expand Up @@ -276,47 +276,38 @@ private static IReadOnlyCollection<ResourceDictionary> EmptyResourceDictionaries

public static IEnumerable<FrameworkElement> GetFrameworkElementOwners(ResourceDictionary dictionary)
{
return GetOwners<FrameworkElement>(dictionary.FrameworkElementOwners, EmptyFrameworkElementList);
return GetOwners(dictionary.FrameworkElementOwners);
}

public static IEnumerable<FrameworkContentElement> GetFrameworkContentElementOwners(ResourceDictionary dictionary)
{
return GetOwners<FrameworkContentElement>(dictionary.FrameworkContentElementOwners, EmptyFrameworkContentElementList);
return GetOwners(dictionary.FrameworkContentElementOwners);
}

public static IEnumerable<Application> GetApplicationOwners(ResourceDictionary dictionary)
{
return GetOwners<Application>(dictionary.ApplicationOwners, EmptyApplicationList);
return GetOwners(dictionary.ApplicationOwners);
}

private static IEnumerable<T> GetOwners<T>(WeakReferenceList list, IEnumerable<T> emptyList)
private static IEnumerable<T> GetOwners<T>(WeakReferenceList<T> list)
where T : DispatcherObject
{
if (!IsEnabled || list == null || list.Count == 0)
{
return emptyList;
return Array.Empty<T>();
}

List<T> result = new List<T>(list.Count);
foreach (Object o in list)
// Copy list manually as it doesn't implement ICollection<T>
List<T> owners = new List<T>(list.Count);
foreach (T item in list)
{
T owner = o as T;
if (owner != null)
{
result.Add(owner);
}
owners.Add(item);
}

return result.AsReadOnly();
// Create a read-only copy of the list
return owners.AsReadOnly();
}

private static IReadOnlyCollection<FrameworkElement> EmptyFrameworkElementList
=> Array.Empty<FrameworkElement>();
private static IReadOnlyCollection<FrameworkContentElement> EmptyFrameworkContentElementList
=> Array.Empty<FrameworkContentElement>();
private static IReadOnlyCollection<Application> EmptyApplicationList
=> Array.Empty<Application>();

#endregion

#region Notify when static resource references are resolved
Expand Down Expand Up @@ -548,7 +539,5 @@ internal class LookupResult

internal static bool IsEnabled { get; private set; }

private static readonly ReadOnlyCollection<ResourceDictionaryInfo> EmptyResourceDictionaryInfos
= new List<ResourceDictionaryInfo>().AsReadOnly();
}
}
Loading