diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs new file mode 100644 index 00000000000..43ea426f946 --- /dev/null +++ b/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs @@ -0,0 +1,55 @@ +// 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. + +// This extension is restricted to the .NET 5 because it shares the same BCL +// across all targets, ensuring that the layout of our Nullable mapping type +// will be correct. Exposing this API on older targets (especially .NET Standard) +// is not guaranteed to be correct and could result in invalid memory accesses. +#if NET5_0 + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Toolkit.HighPerformance.Extensions +{ + /// + /// Helpers for working with the type. + /// + public static class NullableExtensions + { + /// + /// Returns a reference to the value of the input instance, regardless of whether + /// the property is returning or not. If that is not + /// the case, this method will still return a reference to the underlying value. + /// + /// The type of the underlying value + /// The + /// A reference to the underlying value from the input instance. + /// + /// Note that attempting to mutate the returned reference will not change the value returned by . + /// That means that reassigning the value of an empty instance will not make return . + /// + public static ref T DangerousGetValueOrDefaultReference(this ref T? value) + where T : struct + { + return ref Unsafe.As>(ref value).Value; + } + + /// + /// Mapping type that reflects the internal layout of the type. + /// See https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Nullable.cs. + /// + /// The value type wrapped by the current instance. + private struct RawNullableData + where T : struct + { +#pragma warning disable CS0649 // Unassigned fields + public bool HasValue; + public T Value; +#pragma warning restore CS0649 + } + } +} + +#endif \ No newline at end of file diff --git a/UnitTests/UnitTests.HighPerformance.Shared/Extensions/Test_NullableExtensions.cs b/UnitTests/UnitTests.HighPerformance.Shared/Extensions/Test_NullableExtensions.cs new file mode 100644 index 00000000000..dd9386978c5 --- /dev/null +++ b/UnitTests/UnitTests.HighPerformance.Shared/Extensions/Test_NullableExtensions.cs @@ -0,0 +1,42 @@ +// 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. + +#if NET5_0 + +using System.Numerics; +using Microsoft.Toolkit.HighPerformance.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace UnitTests.HighPerformance.Extensions +{ + [TestClass] + public class Test_NullableExtensions + { + [TestCategory("NullableExtensions")] + [TestMethod] + public void Test_NullableExtensions_DangerousGetReference() + { + static void Test(T before, T after) + where T : struct + { + T? nullable = before; + ref T reference = ref nullable.DangerousGetValueOrDefaultReference(); + + Assert.AreEqual(nullable.Value, before); + + reference = after; + + Assert.AreEqual(nullable.Value, after); + } + + Test(0, 42); + Test(1.3f, 3.14f); + Test(0.555, 8.49); + Test(Vector4.Zero, new Vector4(1, 5.55f, 2, 3.14f)); + Test(Matrix4x4.Identity, Matrix4x4.CreateOrthographic(35, 88.34f, 9.99f, 24.6f)); + } + } +} + +#endif diff --git a/UnitTests/UnitTests.HighPerformance.Shared/UnitTests.HighPerformance.Shared.projitems b/UnitTests/UnitTests.HighPerformance.Shared/UnitTests.HighPerformance.Shared.projitems index 2a0bdf27505..e8bd1a2043c 100644 --- a/UnitTests/UnitTests.HighPerformance.Shared/UnitTests.HighPerformance.Shared.projitems +++ b/UnitTests/UnitTests.HighPerformance.Shared/UnitTests.HighPerformance.Shared.projitems @@ -28,6 +28,7 @@ +