-
Notifications
You must be signed in to change notification settings - Fork 7.5k
[Settings] Create a global static instance of SettingsUtils #44064
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a singleton pattern for SettingsUtils by creating a global static instance called GlobalDefaultInstance. The purpose is to avoid repeatedly instantiating SettingsUtils throughout the solution, which should improve performance by reducing redundant object creation and initialization. The PR systematically replaces all new SettingsUtils() calls across the codebase with references to the new singleton instance.
Key Changes
- Added
GlobalDefaultInstancestatic property toSettingsUtilsclass that provides a pre-initialized singleton instance - Changed the parameterless constructor from
publictoprivateto encourage use of the singleton - Updated all production code (Settings UI, modules, preview handlers, DSC, etc.) to use
GlobalDefaultInstanceinstead of creating new instances - Modified unit tests to use parameterized constructors for mocking while maintaining test isolation
Reviewed changes
Copilot reviewed 83 out of 83 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/settings-ui/Settings.UI.Library/SettingsUtils.cs | Implements the singleton pattern with GlobalDefaultInstance property and makes the default constructor private |
| src/settings-ui/Settings.UI/ViewModels/*.cs | Multiple ViewModels updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI/SettingsXAML/Views/*.cs | All page views updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/*.cs | OOBE pages updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI/SettingsXAML/Flyout/*.cs | Flyout pages updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI/Helpers/HotkeyConflictIgnoreHelper.cs | Helper class updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI.UnitTests/ViewModelTests/*.cs | Test files updated to use parameterized constructors with FileSystem for mocking |
| src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs | Command line utilities updated to use GlobalDefaultInstance |
| src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs | Backup utilities updated to use GlobalDefaultInstance |
| src/modules/previewpane/*/Settings.cs | Preview pane modules updated to use GlobalDefaultInstance |
| src/modules/poweraccent/PowerAccent.Core/**/*.cs | PowerAccent module updated to use GlobalDefaultInstance |
| src/modules/peek/Peek./**/.cs | Peek module updated to use GlobalDefaultInstance |
| src/modules/launcher/PowerLauncher/SettingsReader.cs | PowerLauncher updated to use GlobalDefaultInstance |
| src/modules/launcher/Plugins/**/UtilityProvider.cs | Launcher plugin updated to use GlobalDefaultInstance |
| src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs | ColorPicker module updated to use GlobalDefaultInstance |
| src/modules/awake/Awake/**/*.cs | Awake module updated to use GlobalDefaultInstance |
| src/modules/Workspaces/WorkspacesEditor/**/*.cs | Workspaces editor updated to use GlobalDefaultInstance |
| src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs | PowerOCR module updated to use GlobalDefaultInstance |
| src/modules/MouseWithoutBorders/App/Class/Setting.cs | MouseWithoutBorders module updated to use GlobalDefaultInstance |
| src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs | MouseJump utility updated to use GlobalDefaultInstance |
| src/modules/MeasureTool/MeasureToolUI/Settings.cs | MeasureTool module updated to use GlobalDefaultInstance |
| src/modules/Hosts/Hosts/Settings/UserSettings.cs | Hosts module updated to use GlobalDefaultInstance |
| src/dsc/v3/PowerToys.DSC/Models/FunctionData/SettingsFunctionData`1.cs | DSC function data updated to use GlobalDefaultInstance |
| src/dsc/v3/PowerToys.DSC.UnitTests/SettingsResourceTests/SettingsResourceModuleTest`1.cs | DSC test updated to use GlobalDefaultInstance |
| src/common/UITestAutomation/SettingsConfigHelper.cs | UI test automation helper updated to use GlobalDefaultInstance |
Comments suppressed due to low confidence (1)
src/settings-ui/Settings.UI.Library/SettingsUtils.cs:35
- The constructor at line 27 is now
private, but lines 32 and 37 still havepublicconstructors. This means the singleton pattern is not fully enforced - consumers can still create new instances using the public constructors. If the intent is to create a true singleton, all constructors should be private except for a parameterized constructor used for testing/mocking.
private SettingsUtils()
: this(new FileSystem())
{
}
public SettingsUtils(IFileSystem? fileSystem, JsonSerializerOptions? serializerOptions = null)
: this(fileSystem?.File!, new SettingPath(fileSystem?.Directory, fileSystem?.Path), serializerOptions)
{
}
| private readonly JsonSerializerOptions _serializerOptions; | ||
|
|
||
| public SettingsUtils() | ||
| public static SettingsUtils GlobalDefaultInstance { get; } = new SettingsUtils(); |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Consider renaming GlobalDefaultInstance to just Default or Instance to follow more conventional singleton naming patterns (e.g., Lazy<T>.Value, Task.CompletedTask). The "Global" prefix is redundant for a static property, and "Default" is clear from context.
| private readonly IFile _file; | ||
| private readonly ISettingsPath _settingsPath; | ||
| private readonly JsonSerializerOptions _serializerOptions; | ||
|
|
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The introduction of GlobalDefaultInstance should be documented. Consider adding XML documentation comments to explain:
- When to use
GlobalDefaultInstancevs. the parameterized constructor - That
GlobalDefaultInstanceuses the defaultFileSystemimplementation - That tests should use the public constructors with mocked dependencies instead of the global instance
Additionally, the documentation in doc/devdocs/core/settings/settings-implementation.md shows incorrect usage examples that should be updated to reflect this change.
| /// <summary> | |
| /// Provides a global default instance of <see cref="SettingsUtils"/> using the default <see cref="FileSystem"/> implementation. | |
| /// <para> | |
| /// <b>Usage:</b> Use <c>GlobalDefaultInstance</c> for production code where default file system access is required. | |
| /// For unit tests or scenarios requiring custom or mocked dependencies, use the public constructors instead. | |
| /// </para> | |
| /// <para> | |
| /// <b>Note:</b> <c>GlobalDefaultInstance</c> is not suitable for tests; always inject mocked <see cref="IFileSystem"/> and <see cref="JsonSerializerOptions"/> via the constructors. | |
| /// </para> | |
| /// </summary> |
| ISettingsUtilsMocks.GetStubSettingsUtils<ColorPickerSettings>().Object, | ||
| SettingsRepository<GeneralSettings>.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils<GeneralSettings>().Object), | ||
| SettingsRepository<ColorPickerSettings>.GetInstance(new SettingsUtils()), | ||
| SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.GlobalDefaultInstance), |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is mixing mocked settings with the global default instance. Line 68 uses a stubbed ISettingsUtils mock, but line 70 uses SettingsUtils.GlobalDefaultInstance, which will read from the actual file system. This inconsistency may cause test flakiness or failures.
Consider using the same stubbed mock for consistency:
SettingsRepository<ColorPickerSettings>.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils<ColorPickerSettings>().Object),Or if the global instance was intentional, all three parameters should consistently use either mocks or the global instance.
| SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.GlobalDefaultInstance), | |
| SettingsRepository<ColorPickerSettings>.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils<ColorPickerSettings>().Object), |
Summary of the Pull Request
SettingsUtils is initialized multiple times over the whole solution. This creates one singeltone instance (with the default settings), so it only has to be initialized once (and improve performance a bit with that)
PR Checklist
Detailed Description of the Pull Request / Additional comments
Validation Steps Performed