diff --git a/Directory.Build.props b/Directory.Build.props
index 48a18c427..2fb1f6a36 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -25,7 +25,7 @@
-
+
@@ -39,7 +39,7 @@
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
-
+
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 713592b83..81b40fd16 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -34,6 +34,10 @@ jobs:
# Test solution #
+ # Run .NET 8 unit tests
+ - script: dotnet test --no-build -c $(Build.Configuration) -f net8.0 -l "trx;LogFileName=VSTestResults_net8.0.trx"
+ displayName: Run .NET 8 unit tests
+
# Run .NET 7 unit tests
- script: dotnet test --no-build -c $(Build.Configuration) -f net7.0 -l "trx;LogFileName=VSTestResults_net7.0.trx"
displayName: Run .NET 7 unit tests
diff --git a/build/Community.Toolkit.Common.props b/build/Community.Toolkit.Common.props
index a43ae645d..4d0ec4379 100644
--- a/build/Community.Toolkit.Common.props
+++ b/build/Community.Toolkit.Common.props
@@ -20,7 +20,7 @@
true
- 11.0
+ 12.0
enable
$(Product) Asset
diff --git a/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj b/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj
index f2d7cb70b..ddf81b30c 100644
--- a/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj
+++ b/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;net6.0
+ netstandard2.0;netstandard2.1;net6.0;net8.0
diff --git a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj
index 6cd510f16..fec859625 100644
--- a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj
+++ b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;net6.0
+ netstandard2.0;netstandard2.1;net6.0;net8.0
diff --git a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj
index bfc0e6bbe..2cf7770af 100644
--- a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj
+++ b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;net6.0;net7.0
+ netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0
diff --git a/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs
index 98fe1f38b..f4a6cac3a 100644
--- a/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs
+++ b/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs
@@ -48,7 +48,7 @@ public static ref T DangerousGetValueOrDefaultReference(this ref T? value)
/// The type of the underlying value.
/// The .
/// A reference to the value of the input instance, or a reference.
- /// The returned reference can be tested for using .
+ /// The returned reference can be tested for using .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T DangerousGetValueOrNullReference(ref this T? value)
where T : struct
diff --git a/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
index 0e78fc341..dfae52a13 100644
--- a/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
+++ b/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
@@ -213,7 +213,7 @@ public static ReadOnlySpan2D AsSpan2D(this ReadOnlySpan span, int offse
public static unsafe int IndexOf(this ReadOnlySpan span, in T value)
{
ref T r0 = ref MemoryMarshal.GetReference(span);
- ref T r1 = ref Unsafe.AsRef(value);
+ ref T r1 = ref Unsafe.AsRef(in value);
IntPtr byteOffset = Unsafe.ByteOffset(ref r0, ref r1);
nint elementOffset = byteOffset / (nint)(uint)sizeof(T);
diff --git a/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs
index a4808e15b..500c65001 100644
--- a/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs
+++ b/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs
@@ -275,7 +275,7 @@ public static unsafe void Write(this Stream stream, in T value)
where T : unmanaged
{
#if NETSTANDARD2_1_OR_GREATER
- ref T r0 = ref Unsafe.AsRef(value);
+ ref T r0 = ref Unsafe.AsRef(in value);
ref byte r1 = ref Unsafe.As(ref r0);
int length = sizeof(T);
diff --git a/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs
index a3260c5bd..b2875637c 100644
--- a/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs
+++ b/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs
@@ -27,7 +27,7 @@ public static class StringExtensions
public static ref char DangerousGetReference(this string text)
{
#if NET6_0_OR_GREATER
- return ref Unsafe.AsRef(text.GetPinnableReference());
+ return ref Unsafe.AsRef(in text.GetPinnableReference());
#else
return ref MemoryMarshal.GetReference(text.AsSpan());
#endif
@@ -44,7 +44,7 @@ public static ref char DangerousGetReference(this string text)
public static ref char DangerousGetReferenceAt(this string text, int i)
{
#if NET6_0_OR_GREATER
- ref char r0 = ref Unsafe.AsRef(text.GetPinnableReference());
+ ref char r0 = ref Unsafe.AsRef(in text.GetPinnableReference());
#else
ref char r0 = ref MemoryMarshal.GetReference(text.AsSpan());
#endif
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs
index 5a8fe49d5..4ea95a236 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs
@@ -172,7 +172,7 @@ public static void For(int start, int end, in TAction action, int minim
{
for (int i = start; i < end; i++)
{
- Unsafe.AsRef(action).Invoke(i);
+ Unsafe.AsRef(in action).Invoke(i);
}
return;
@@ -225,7 +225,7 @@ public void Invoke(int i)
for (int j = low; j < stop; j++)
{
- Unsafe.AsRef(this.action).Invoke(j);
+ Unsafe.AsRef(in this.action).Invoke(j);
}
}
}
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs
index 6a16c8ca1..66009ea7b 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs
@@ -257,7 +257,7 @@ public static void For2D(int top, int bottom, int left, int right, in T
{
for (int x = left; x < right; x++)
{
- Unsafe.AsRef(action).Invoke(y, x);
+ Unsafe.AsRef(in action).Invoke(y, x);
}
}
@@ -319,7 +319,7 @@ public void Invoke(int i)
{
for (int x = this.startX; x < this.endX; x++)
{
- Unsafe.AsRef(this.action).Invoke(y, x);
+ Unsafe.AsRef(in this.action).Invoke(y, x);
}
}
}
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs
index 7d4ae7a7c..f0e4790af 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs
@@ -91,7 +91,7 @@ public static void ForEach(ReadOnlyMemory memory, in TAct
{
foreach (TItem? item in memory.Span)
{
- Unsafe.AsRef(action).Invoke(item);
+ Unsafe.AsRef(in action).Invoke(item);
}
return;
@@ -144,7 +144,7 @@ public void Invoke(int i)
while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd))
{
- Unsafe.AsRef(this.action).Invoke(in rStart);
+ Unsafe.AsRef(in this.action).Invoke(in rStart);
rStart = ref Unsafe.Add(ref rStart, 1);
}
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
index e10361c16..a600c529c 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
@@ -144,7 +144,7 @@ public void Invoke(int i)
while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd))
{
- Unsafe.AsRef(this.action).Invoke(in rStart);
+ Unsafe.AsRef(in this.action).Invoke(in rStart);
rStart = ref Unsafe.Add(ref rStart, 1);
}
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs
index ca990f4fc..86ed6290f 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs
@@ -91,7 +91,7 @@ public static void ForEach(Memory memory, in TAction acti
{
foreach (ref TItem item in memory.Span)
{
- Unsafe.AsRef(action).Invoke(ref item);
+ Unsafe.AsRef(in action).Invoke(ref item);
}
return;
@@ -144,7 +144,7 @@ public void Invoke(int i)
while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd))
{
- Unsafe.AsRef(this.action).Invoke(ref rStart);
+ Unsafe.AsRef(in this.action).Invoke(ref rStart);
rStart = ref Unsafe.Add(ref rStart, 1);
}
diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
index 4a6bfc28c..799f26d69 100644
--- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
+++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
@@ -151,7 +151,7 @@ public void Invoke(int i)
while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd))
{
- Unsafe.AsRef(this.action).Invoke(ref rStart);
+ Unsafe.AsRef(in this.action).Invoke(ref rStart);
rStart = ref Unsafe.Add(ref rStart, 1);
}
diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
index 272f56c7f..e6c7d8be2 100644
--- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
+++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;net6.0
+ netstandard2.0;netstandard2.1;net6.0;net8.0
@@ -22,7 +22,7 @@
-
+
@@ -55,6 +55,7 @@
System.Diagnostics.CodeAnalysis.NotNullAttribute;
System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute;
System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;
+ System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute;
System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute;
System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute;
System.Runtime.CompilerServices.CallerArgumentExpressionAttribute;
@@ -65,7 +66,7 @@
-
+
diff --git a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs
index 63b28441b..5cd9433e6 100644
--- a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs
+++ b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs
@@ -64,6 +64,12 @@ public bool IsActive
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " +
"Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
+ [RequiresDynamicCode(
+ "When this property is set to true, the OnActivated() method will be invoked, which will register all necessary message handlers for this recipient. " +
+ "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
+ "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " +
+ "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime. " +
+ "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
set
{
if (SetProperty(ref this.isActive, value, true))
@@ -96,6 +102,11 @@ public bool IsActive
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " +
"Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
+ [RequiresDynamicCode(
+ "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
+ "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " +
+ "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime. " +
+ "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")]
protected virtual void OnActivated()
{
Messenger.RegisterAll(this);
diff --git a/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs b/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs
index f6f038166..6febfb2e1 100644
--- a/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs
+++ b/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs
@@ -90,6 +90,10 @@ public static bool IsRegistered(this IMessenger messenger, object reci
"This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type.")]
+ [RequiresDynamicCode(
+ "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
+ "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " +
+ "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime.")]
public static void RegisterAll(this IMessenger messenger, object recipient)
{
ArgumentNullException.ThrowIfNull(messenger);
@@ -113,7 +117,7 @@ public static void RegisterAll(this IMessenger messenger, object recipient)
// Try to get the cached delegate, if the generator has run correctly
Action? registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue(
recipient.GetType(),
- [RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")] static (t) => LoadRegistrationMethodsForType(t));
+ LoadRegistrationMethodsForType);
if (registrationAction is not null)
{
@@ -144,6 +148,7 @@ public static void RegisterAll(this IMessenger messenger, object recipient)
"This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " +
"If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " +
"path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type.")]
+ [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")]
public static void RegisterAll(this IMessenger messenger, object recipient, TToken token)
where TToken : IEquatable
{
@@ -156,6 +161,7 @@ public static void RegisterAll(this IMessenger messenger, object recipie
// target recipient type, and just invoke it to get the delegate to cache and use later.
// In this case we also need to create a generic instantiation of the target method first.
[RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")]
+ [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")]
static Action LoadRegistrationMethodsForType(Type recipientType)
{
if (recipientType.Assembly.GetType("CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions") is Type extensionsType &&
@@ -173,6 +179,7 @@ static Action LoadRegistrationMethodsForType(Type re
// This method is only invoked once per recipient type and token type, so we're not
// worried about making it super efficient, and we can use the LINQ code for clarity.
// The LINQ codegen bloat is not really important for the same reason.
+ [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")]
static Action LoadRegistrationMethodsForTypeFallback(Type recipientType)
{
// Get the collection of validation methods
@@ -231,7 +238,7 @@ from registrationMethod in registrationMethods
// For more info on this, see the related issue at https://github.com/dotnet/roslyn/issues/5835.
Action registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue(
recipient.GetType(),
- [RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")] static (t) => LoadRegistrationMethodsForType(t));
+ LoadRegistrationMethodsForType);
// Invoke the cached delegate to actually execute the message registration
registrationAction(messenger, recipient, token);
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 882db66f3..486e75379 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,7 +3,7 @@
-
+
all
build; analyzers
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index b070ed1b8..23f65c476 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -3,18 +3,23 @@
-
+
NETSTANDARD2_1_OR_GREATER
-
+
true
true
true
true
+
+
+ true
+
+