diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 233d19f0d5b5f4..06cdf585cced69 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -307,6 +307,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 0302bc2bc649dd..6d123da2b4792d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -278,6 +278,9 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) => Math.DivRem(left, right); + /// + public static (byte Quotient, byte Remainder) DivRem(byte left, byte right, DivisionRounding rounding) => Math.DivRem(left, right); + /// public static byte LeadingZeroCount(byte value) => (byte)(BitOperations.LeadingZeroCount(value) - 24); diff --git a/src/libraries/System.Private.CoreLib/src/System/DivisionRounding.cs b/src/libraries/System.Private.CoreLib/src/System/DivisionRounding.cs new file mode 100644 index 00000000000000..268e514d0883ac --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/DivisionRounding.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + public enum DivisionRounding + { + Truncate = 0, + Floor = 1, + Ceiling = 2, + AwayFromZero = 3, + Euclidean = 4, + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index f71b84138ecfb5..91a9850cea283c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -296,6 +296,9 @@ object IConvertible.ToType(Type type, IFormatProvider? provider) /// public static (int Quotient, int Remainder) DivRem(int left, int right) => Math.DivRem(left, right); + /// + public static (int Quotient, int Remainder) DivRem(int left, int right, DivisionRounding rounding) => Math.DivRem(left, right, rounding); + /// [Intrinsic] public static int LeadingZeroCount(int value) => BitOperations.LeadingZeroCount((uint)value); diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 32c3eee4712bfc..5a85489ad6be10 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -501,6 +501,129 @@ public static (nuint Quotient, nuint Remainder) DivRem(nuint left, nuint right) return (quotient, left - (quotient * right)); } + /// Produces the quotient and the remainder of two signed 32-bit numbers. + /// The dividend. + /// The divisor. + /// The rounding type. + /// The quotient and the remainder of the specified numbers. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (int Quotient, int Remainder) DivRem(int left, int right, DivisionRounding rounding) + { + switch (rounding) + { + case DivisionRounding.Truncate: + return DivRem(left, right); + case DivisionRounding.Floor: + return DivRemFloor(left, right); + case DivisionRounding.Ceiling: + return DivRemCeiling(left, right); + case DivisionRounding.AwayFromZero: + return DivRemAwayFromZero(left, right); + case DivisionRounding.Euclidean: + return DivRemEuclidean(left, right); + default: + ThrowHelper.ThrowArgumentException_InvalidEnumValue(rounding); + return default; + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int quotient, int remainder) DivRemFloor(int left, int right) + { + int quotient = left / right; + int remainder = left - (quotient * right); + + if (remainder != 0) + { + return FloorRounding(left, right, quotient, remainder); + } + + return (quotient, remainder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int quotient, int remainder) FloorRounding(int left, int right, int quotient, int remainder) + { + if (Sign(left) != Sign(right)) + { + quotient--; + remainder += right; + } + + return (quotient, remainder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int Quotient, int Remainder) DivRemCeiling(int left, int right) + { + int quotient = left / right; + int remainder = left - (quotient * right); + + if (remainder != 0) + { + return CeilingRounding(left, right, quotient, remainder); + } + + return (quotient, remainder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int quotient, int remainder) CeilingRounding(int left, int right, int quotient, int remainder) + { + if (Sign(left) == Sign(right)) + { + quotient++; + remainder -= right; + } + + return (quotient, remainder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int Quotient, int Remainder) DivRemAwayFromZero(int left, int right) + { + int quotient = left / right; + int remainder = left - (quotient * right); + + if (remainder != 0) + { + if (Sign(left) == Sign(right)) + { + quotient++; + } + else + { + quotient--; + } + } + + remainder = left - (quotient * right); + + return (quotient, remainder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int Quotient, int Remainder) DivRemEuclidean(int left, int right) + { + int quotient = left / right; + int remainder = left - (quotient * right); + + if (remainder != 0) + { + if (right > 0) + { + return FloorRounding(left, right, quotient, remainder); + } + else + { + return CeilingRounding(left, right, quotient, remainder); + } + } + + return (quotient, remainder); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal Ceiling(decimal d) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs index a150b61e2c40e4..abfe1b6604518e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs @@ -20,6 +20,125 @@ static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) return (quotient, (left - (quotient * right))); } + /// + /// Computes the quotient and remainder of two values. + /// Throws an if is invalid. + /// + /// The value which divides. + /// The value which divides . + /// + /// The quotient and remainder of divided-by . + /// + static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right, DivisionRounding rounding) + { + switch (rounding) + { + case DivisionRounding.Truncate: + return TSelf.DivRem(left, right); + case DivisionRounding.Floor: + return DivRemFloor(left, right); + case DivisionRounding.Ceiling: + return DivRemCeiling(left, right); + case DivisionRounding.AwayFromZero: + return DivRemAwayFromZero(left, right); + case DivisionRounding.Euclidean: + return DivRemEuclidean(left, right); + default: + ThrowHelper.ThrowArgumentException_InvalidEnumValue(rounding); + return default; + }; + } + + private static (TSelf quotient, TSelf remainder) DivRemFloor(TSelf left, TSelf right) + { + TSelf quotient = left / right; + TSelf remainder = left - (quotient * right); + + if (remainder != TSelf.Zero) + { + return FloorRounding(left, right, quotient, remainder); + } + + return (quotient, remainder); + } + + private static (TSelf quotient, TSelf remainder) FloorRounding(TSelf left, TSelf right, TSelf quotient, TSelf remainder) + { + if (TSelf.Sign(left) != TSelf.Sign(right)) + { + quotient--; + remainder += right; + } + + return (quotient, remainder); + } + + private static (TSelf Quotient, TSelf Remainder) DivRemCeiling(TSelf left, TSelf right) + { + TSelf quotient = left / right; + TSelf remainder = left - (quotient * right); + + if (remainder != TSelf.Zero) + { + return CeilingRounding(left, right, quotient, remainder); + } + + return (quotient, remainder); + } + + private static (TSelf quotient, TSelf remainder) CeilingRounding(TSelf left, TSelf right, TSelf quotient, TSelf remainder) + { + if (TSelf.Sign(left) == TSelf.Sign(right)) + { + quotient++; + remainder -= right; + } + + return (quotient, remainder); + } + + private static (TSelf Quotient, TSelf Remainder) DivRemAwayFromZero(TSelf left, TSelf right) + { + TSelf quotient = left / right; + TSelf remainder = left - (quotient * right); + + if (remainder != TSelf.Zero) + { + if (TSelf.Sign(left) == TSelf.Sign(right)) + { + quotient++; + } + else + { + quotient--; + } + } + + remainder = left - (quotient * right); + + return (quotient, remainder); + } + + private static (TSelf Quotient, TSelf Remainder) DivRemEuclidean(TSelf left, TSelf right) + { + TSelf quotient = left / right; + TSelf remainder = left - (quotient * right); + + if (remainder != TSelf.Zero) + { + if (right > TSelf.Zero) + { + return FloorRounding(left, right, quotient, remainder); + } + else + { + return CeilingRounding(left, right, quotient, remainder); + } + } + + return (quotient, remainder); + } + /// Computes the number of leading zero bits in a value. /// The value whose leading zero bits are to be counted. /// The number of leading zero bits in . diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index e7e61105315e09..28f707c2f0ad29 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -944,6 +944,7 @@ public static void SetByte(System.Array array, int index, byte value) { } public static byte CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static byte CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) { throw null; } + public static (byte Quotient, byte Remainder) DivRem(byte left, byte right, System.DivisionRounding rounding) { throw null; } public bool Equals(byte obj) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } @@ -2369,6 +2370,14 @@ protected DivideByZeroException(System.Runtime.Serialization.SerializationInfo i public DivideByZeroException(string? message) { } public DivideByZeroException(string? message, System.Exception? innerException) { } } + public enum DivisionRounding + { + Truncate = 0, + Floor = 1, + Ceiling = 2, + AwayFromZero = 3, + Euclidean = 4, + } public readonly partial struct Double : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { private readonly double _dummyPrimitive; @@ -3886,6 +3895,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static int CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static int CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static (int Quotient, int Remainder) DivRem(int left, int right) { throw null; } + public static (int Quotient, int Remainder) DivRem(int left, int right, System.DivisionRounding rounding) { throw null; } public bool Equals(int obj) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } public override int GetHashCode() { throw null; } @@ -4471,6 +4481,7 @@ public static partial class Math public static (byte Quotient, byte Remainder) DivRem(byte left, byte right) { throw null; } public static (short Quotient, short Remainder) DivRem(short left, short right) { throw null; } public static (int Quotient, int Remainder) DivRem(int left, int right) { throw null; } + public static (int Quotient, int Remainder) DivRem(int left, int right, System.DivisionRounding rounding) { throw null; } public static int DivRem(int a, int b, out int result) { throw null; } public static (long Quotient, long Remainder) DivRem(long left, long right) { throw null; } public static long DivRem(long a, long b, out long result) { throw null; } @@ -11138,6 +11149,7 @@ public partial interface IBinaryFloatingPointIeee754 : System.IComparable public partial interface IBinaryInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators where TSelf : System.Numerics.IBinaryInteger? { static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right) { throw null; } + static virtual (TSelf Quotient, TSelf Remainder) DivRem(TSelf left, TSelf right, System.DivisionRounding rounding) { throw null; } int GetByteCount(); int GetShortestBitLength(); static virtual TSelf LeadingZeroCount(TSelf value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Math.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Math.cs index 7d8606b799d649..54ddd757f809f3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Math.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Math.cs @@ -1939,6 +1939,95 @@ public static void DivRemInt32(int dividend, int divisor, int expectedQuotient, } } + [Theory] + // Truncate + [InlineData(2147483647, 2000, 1073741, 1647, DivisionRounding.Truncate)] + [InlineData(13952, 2000, 6, 1952, DivisionRounding.Truncate)] + [InlineData(0, 2000, 0, 0, DivisionRounding.Truncate)] + [InlineData(-14032, 2000, -7, -32, DivisionRounding.Truncate)] + [InlineData(-2147483648, 2000, -1073741, -1648, DivisionRounding.Truncate)] + [InlineData(2147483647, -2000, -1073741, 1647, DivisionRounding.Truncate)] + [InlineData(13952, -2000, -6, 1952, DivisionRounding.Truncate)] + [InlineData(13952, 0, 0, 0, DivisionRounding.Truncate)] + [InlineData(int.MaxValue, int.MaxValue, 1, 0, DivisionRounding.Truncate)] + [InlineData(int.MaxValue, 1, int.MaxValue, 0, DivisionRounding.Truncate)] + [InlineData(int.MaxValue, 0, 0, 0, DivisionRounding.Truncate)] + [InlineData(1, int.MaxValue, 0, 1, DivisionRounding.Truncate)] + [InlineData(0, 0, 0, 0, DivisionRounding.Truncate)] + // Floor Tests + [InlineData(2147483647, 2000, 1073741, 1647, DivisionRounding.Floor)] + [InlineData(13952, 2000, 6, 1952, DivisionRounding.Floor)] + [InlineData(0, 2000, 0, 0, DivisionRounding.Floor)] + [InlineData(-14032, 2000, -8, 1968, DivisionRounding.Floor)] + [InlineData(-2147483648, 2000, -1073742, 352, DivisionRounding.Floor)] + [InlineData(2147483647, -2000, -1073742, -353, DivisionRounding.Floor)] + [InlineData(13952, -2000, -7, -48, DivisionRounding.Floor)] + [InlineData(13952, 0, 0, 0, DivisionRounding.Floor)] + [InlineData(int.MaxValue, 0, 0, 0, DivisionRounding.Floor)] + [InlineData(int.MaxValue, 1, int.MaxValue, 0, DivisionRounding.Floor)] + [InlineData(int.MaxValue, int.MaxValue, 1, 0, DivisionRounding.Floor)] + [InlineData(1, int.MaxValue, 0, 1, DivisionRounding.Floor)] + [InlineData(0, 0, 0, 0, DivisionRounding.Floor)] + // Ceiling Tests + [InlineData(2147483647, 2000, 1073742, -353, DivisionRounding.Ceiling)] + [InlineData(13952, 2000, 7, -48, DivisionRounding.Ceiling)] + [InlineData(0, 2000, 0, 0, DivisionRounding.Ceiling)] + [InlineData(-14032, 2000, -7, -32, DivisionRounding.Ceiling)] + [InlineData(-2147483648, 2000, -1073741, -1648, DivisionRounding.Ceiling)] + [InlineData(2147483647, -2000, -1073741, 1647, DivisionRounding.Ceiling)] + [InlineData(13952, -2000, -6, 1952, DivisionRounding.Ceiling)] + [InlineData(13952, 0, 0, 0, DivisionRounding.Ceiling)] + [InlineData(int.MaxValue, 0, 0, 0, DivisionRounding.Ceiling)] + [InlineData(int.MaxValue, 1, int.MaxValue, 0, DivisionRounding.Ceiling)] + [InlineData(int.MaxValue, int.MaxValue, 1, 0, DivisionRounding.Ceiling)] + [InlineData(1, int.MaxValue, 1, -2147483646, DivisionRounding.Ceiling)] + [InlineData(0, 0, 0, 0, DivisionRounding.Ceiling)] + // AwayFromZero Tests + [InlineData(2147483647, 2000, 1073742, -353, DivisionRounding.AwayFromZero)] + [InlineData(13952, 2000, 7, -48, DivisionRounding.AwayFromZero)] + [InlineData(0, 2000, 0, 0, DivisionRounding.AwayFromZero)] + [InlineData(-14032, 2000, -8, 1968, DivisionRounding.AwayFromZero)] + [InlineData(-2147483648, 2000, -1073742, 352, DivisionRounding.AwayFromZero)] + [InlineData(2147483647, -2000, -1073742, -353, DivisionRounding.AwayFromZero)] + [InlineData(13952, -2000, -7, -48, DivisionRounding.AwayFromZero)] + [InlineData(13952, 0, 0, 0, DivisionRounding.AwayFromZero)] + [InlineData(int.MaxValue, 0, 0, 0, DivisionRounding.AwayFromZero)] + [InlineData(int.MaxValue, 1, int.MaxValue, 0, DivisionRounding.AwayFromZero)] + [InlineData(int.MaxValue, int.MaxValue, 1, 0, DivisionRounding.AwayFromZero)] + [InlineData(1, int.MaxValue, 1, -2147483646, DivisionRounding.AwayFromZero)] + [InlineData(0, 0, 0, 0, DivisionRounding.AwayFromZero)] + // Euclidean Tests + [InlineData(2147483647, 2000, 1073741, 1647, DivisionRounding.Euclidean)] + [InlineData(13952, 2000, 6, 1952, DivisionRounding.Euclidean)] + [InlineData(0, 2000, 0, 0, DivisionRounding.Euclidean)] + [InlineData(-14032, 2000, -8, 1968, DivisionRounding.Euclidean)] + [InlineData(-2147483648, 2000, -1073742, 352, DivisionRounding.Euclidean)] + [InlineData(2147483647, -2000, -1073741, 1647, DivisionRounding.Euclidean)] + [InlineData(13952, -2000, -6, 1952, DivisionRounding.Euclidean)] + [InlineData(13952, 0, 0, 0, DivisionRounding.Euclidean)] + [InlineData(int.MaxValue, 0, 0, 0, DivisionRounding.Euclidean)] + [InlineData(int.MaxValue, 1, int.MaxValue, 0, DivisionRounding.Euclidean)] + [InlineData(int.MaxValue, int.MaxValue, 1, 0, DivisionRounding.Euclidean)] + [InlineData(1, int.MaxValue, 0, 1, DivisionRounding.Euclidean)] + [InlineData(0, 0, 0, 0, DivisionRounding.Euclidean)] + public static void DivRemWithRoundingInt32(int dividend, int divisor, int expectedQuotient, int expectedRemainder, DivisionRounding divisionRounding) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor, divisionRounding)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor, divisionRounding); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + if (IntPtr.Size == 4) + { + // DivRemWithRoundingNativeInt(dividend, divisor, expectedQuotient, expectedRemainder, divisionRounding); + } + } + [Theory] [InlineData(uint.MaxValue, uint.MaxValue, 1, 0)] [InlineData(uint.MaxValue, 1, uint.MaxValue, 0)] @@ -2050,6 +2139,25 @@ private static void DivRemNativeInt(nint dividend, nint divisor, nint expectedQu } } + /* + * Not yet implemented + * + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DivRemWithRoundingNativeInt(nint dividend, nint divisor, nint expectedQuotient, nint expectedRemainder, DivisionRounding divisionRounding) + { + if (divisor == 0) + { + Assert.Throws(() => Math.DivRem(dividend, divisor, divisionRounding)); + } + else + { + var (actualQuotient, actualRemainder) = Math.DivRem(dividend, divisor, divisionRounding); + Assert.Equal(expectedQuotient, actualQuotient); + Assert.Equal(expectedRemainder, actualRemainder); + } + } + */ + [MethodImpl(MethodImplOptions.NoInlining)] private static void DivRemNativeUInt(nuint dividend, nuint divisor, nuint expectedQuotient, nuint expectedRemainder) { diff --git a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml index 2ce9abc316b77d..0dbd35bc851b16 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml @@ -1,6 +1,7 @@  + CP0014 M:System.ComponentModel.DesignerAttribute.#ctor(System.String,System.String)$0:[T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute]