diff --git a/dotnet Community Toolkit.sln b/dotnet Community Toolkit.sln
index e8d004357..688e32367 100644
--- a/dotnet Community Toolkit.sln
+++ b/dotnet Community Toolkit.sln
@@ -53,7 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CD16E790
build\Update-Headers.ps1 = build\Update-Headers.ps1
EndProjectSection
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{6640D447-C28D-4DBB-91F4-3ADCE0CA64AD}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FeatureSwitches", "FeatureSwitches", "{6640D447-C28D-4DBB-91F4-3ADCE0CA64AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests", "tests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests.csproj", "{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}"
EndProject
@@ -81,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.Exter
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431", "tests\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431\CommunityToolkit.Mvvm.ExternalAssembly.Roslyn431.csproj", "{4FCD501C-1BB5-465C-AD19-356DAB6600C6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.CodeFixers", "src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.CodeFixers", "src\CommunityToolkit.Mvvm.CodeFixers\CommunityToolkit.Mvvm.CodeFixers.csproj", "{E79DCA2A-4C59-499F-85BD-F45215ED6B72}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.FeatureSwitches.targets b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.FeatureSwitches.targets
new file mode 100644
index 000000000..19c3c56c0
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.FeatureSwitches.targets
@@ -0,0 +1,21 @@
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.SourceGenerators.targets b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.SourceGenerators.targets
new file mode 100644
index 000000000..aa4b8966f
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.SourceGenerators.targets
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @(MVVMToolkitCurrentCompilerAssemblyIdentity->'%(Version)')
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+ roslyn4.3
+ roslyn4.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
index 2c1f713e2..b543c3e5f 100644
--- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
+++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
@@ -54,6 +54,11 @@
+
+
+
+
+
@@ -91,11 +96,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @(MVVMToolkitCurrentCompilerAssemblyIdentity->'%(Version)')
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
-
- roslyn4.3
- roslyn4.0
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
+
+
+ <_CommunityToolkitMvvmFeatureSwitchesTargets>$(MSBuildThisFileDirectory)CommunityToolkit.Mvvm.FeatureSwitches.targets
+ <_CommunityToolkitMvvmSourceGeneratorsTargets>$(MSBuildThisFileDirectory)CommunityToolkit.Mvvm.SourceGenerators.targets
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableObject.cs b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableObject.cs
index f3252a291..895f60311 100644
--- a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableObject.cs
+++ b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableObject.cs
@@ -57,6 +57,12 @@ protected virtual void OnPropertyChanging(PropertyChangingEventArgs e)
{
ArgumentNullException.ThrowIfNull(e);
+ // When support is disabled, just do nothing
+ if (!FeatureSwitches.EnableINotifyPropertyChangingSupport)
+ {
+ return;
+ }
+
PropertyChanging?.Invoke(this, e);
}
@@ -75,7 +81,8 @@ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
/// (optional) The name of the property that changed.
protected void OnPropertyChanging([CallerMemberName] string? propertyName = null)
{
- if (Configuration.IsINotifyPropertyChangingDisabled)
+ // When support is disabled, avoid instantiating the event args entirely
+ if (!FeatureSwitches.EnableINotifyPropertyChangingSupport)
{
return;
}
diff --git a/src/CommunityToolkit.Mvvm/Properties/Configuration.cs b/src/CommunityToolkit.Mvvm/Properties/Configuration.cs
deleted file mode 100644
index d0ca0ade0..000000000
--- a/src/CommunityToolkit.Mvvm/Properties/Configuration.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.ComponentModel;
-
-///
-/// A container for all shared configuration switches for the MVVM Toolkit.
-///
-internal static class Configuration
-{
- ///
- /// The configuration property name for .
- ///
- private const string DisableINotifyPropertyChangingSupport = "MVVMTOOLKIT_DISABLE_INOTIFYPROPERTYCHANGING";
-
- ///
- /// Indicates whether or not support for is disabled.
- ///
- public static readonly bool IsINotifyPropertyChangingDisabled = GetConfigurationValue(DisableINotifyPropertyChangingSupport);
-
- ///
- /// Gets a configuration value for a specified property.
- ///
- /// The property name to retrieve the value for.
- /// The value of the specified configuration setting.
- private static bool GetConfigurationValue(string propertyName)
- {
- if (AppContext.TryGetSwitch(propertyName, out bool isEnabled))
- {
- return isEnabled;
- }
-
- return false;
- }
-}
diff --git a/src/CommunityToolkit.Mvvm/Properties/FeatureSwitches.cs b/src/CommunityToolkit.Mvvm/Properties/FeatureSwitches.cs
new file mode 100644
index 000000000..13d9ef595
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm/Properties/FeatureSwitches.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace CommunityToolkit.Mvvm;
+
+///
+/// A container for all shared configuration switches for the MVVM Toolkit.
+///
+///
+///
+/// This type uses a very specific setup for configuration switches to ensure ILLink can work the best.
+/// This mirrors the architecture of feature switches in the runtime as well, and it's needed so that
+/// no static constructor is generated for the type.
+///
+///
+/// For more info, see .
+///
+///
+internal static class FeatureSwitches
+{
+ ///
+ /// The configuration property name for .
+ ///
+ private const string EnableINotifyPropertyChangingSupportPropertyName = "MVVMTOOLKIT_ENABLE_INOTIFYPROPERTYCHANGING_SUPPORT";
+
+ ///
+ /// The backing field for .
+ ///
+ private static int enableINotifyPropertyChangingSupport;
+
+ ///
+ /// Gets a value indicating whether or not support for should be enabled (defaults to ).
+ ///
+ public static bool EnableINotifyPropertyChangingSupport
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetConfigurationValue(EnableINotifyPropertyChangingSupportPropertyName, ref enableINotifyPropertyChangingSupport, true);
+ }
+
+ ///
+ /// Gets a configuration value for a specified property.
+ ///
+ /// The property name to retrieve the value for.
+ /// The cached result for the target configuration value.
+ /// The default value for the feature switch, if not set.
+ /// The value of the specified configuration setting.
+ private static bool GetConfigurationValue(string propertyName, ref int cachedResult, bool defaultValue)
+ {
+ // The cached switch value has 3 states:
+ // 0: unknown.
+ // 1: true
+ // -1: false
+ //
+ // This method doesn't need to worry about concurrent accesses to the cached result,
+ // as even if the configuration value is retrieved twice, that'll always be the same.
+ if (cachedResult < 0)
+ {
+ return false;
+ }
+
+ if (cachedResult > 0)
+ {
+ return true;
+ }
+
+ // Get the configuration switch value, or its default.
+ // All feature switches have a default set in the .targets file.
+ if (!AppContext.TryGetSwitch(propertyName, out bool isEnabled))
+ {
+ isEnabled = defaultValue;
+ }
+
+ // Update the cached result
+ cachedResult = isEnabled ? 1 : -1;
+
+ return isEnabled;
+ }
+}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Mvvm/Properties/ILLink.Substitutions.xml b/src/CommunityToolkit.Mvvm/Properties/ILLink.Substitutions.xml
new file mode 100644
index 000000000..a934a249c
--- /dev/null
+++ b/src/CommunityToolkit.Mvvm/Properties/ILLink.Substitutions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests/Test_DisableINotifyPropertyChanging.cs b/tests/CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests/Test_DisableINotifyPropertyChanging.cs
index c5fc2c4a0..d2a215455 100644
--- a/tests/CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests/Test_DisableINotifyPropertyChanging.cs
+++ b/tests/CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests/Test_DisableINotifyPropertyChanging.cs
@@ -14,7 +14,7 @@ public class Test_DisableINotifyPropertyChanging
{
static Test_DisableINotifyPropertyChanging()
{
- AppContext.SetSwitch("MVVMTOOLKIT_DISABLE_INOTIFYPROPERTYCHANGING", true);
+ AppContext.SetSwitch("MVVMTOOLKIT_ENABLE_INOTIFYPROPERTYCHANGING_SUPPORT", false);
}
[TestMethod]