c#中检查盒装原语整型数值的最快方法
本文关键字:方法 整型 检查 盒装 原语 | 更新日期: 2023-09-27 18:10:31
我需要编写一个具有以下语义的方法:
/// <summary>
/// Checks if <paramref name="x"/> is a boxed instance of a primitive integral type
/// whose numerical value equals to <paramref name="y"/>.
/// </summary>
/// <param name="x">An object reference. Can be <c>null</c>.</param>
/// <param name="y">A numerical value of type <see cref="ulong"/> to compare with.</param>
/// <returns>
/// <c>true</c> if <paramref name="x"/> refers to a boxed instance of type
/// <see cref="sbyte"/>, <see cref="short"/>, <see cref="int"/>, <see cref="long"/>,
/// <see cref="byte"/>, <see cref="ushort"/>, <see cref="uint"/>, or <see cref="ulong"/>,
/// whose numerical value equals to the numerical value of <paramref name="y"/>; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// <para>
/// This method checks only for numeric equality, even if its arguments are of different runtime types
/// (e.g. <c>2L</c> is considered to be equal to <c>2UL</c>).
/// </para>
/// <para>
/// This method returns <c>false</c> if <paramref name="x"/> is <c>null</c>
/// or refers to an instance of a reference type or a boxed instance of a value type except
/// the primitive integral types listed above (e.g. it returns <c>false</c> if <paramref name="x"/>
/// refers to a boxed instance of an <c>enum</c> type, <see cref="bool"/>, <see cref="char"/>, <see cref="IntPtr"/>,
/// <see cref="UIntPtr"/>, <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>, or <see cref="BigInteger"/>).
/// </para>
/// <para>
/// This method should not throw any exceptions, or cause any observable side-effects
/// (e.g. invoke a method that could modify the state of an object referenced by <paramref name="x"/>).
/// </para>
/// </remarks>
[Pure]
public static bool NumericalEquals(object x, ulong y)
实现应该尽可能快(假设输入数据没有对参数x
的某些类型或值的预期偏差),并且不应该使用unsafe
代码或p/Invoke。当然,在最快的实现中,我更喜欢最简单和最短的实现。
我的解决方案如下:
public static bool NumericalEquals(object x, ulong y)
{
if (x is sbyte)
{
sbyte z = (sbyte)x;
return z >= 0 && y == (ulong)z;
}
if (x is short)
{
short z = (short)x;
return z >= 0 && y == (ulong)z;
}
if (x is int)
{
int z = (int)x;
return z >= 0 && y == (ulong)z;
}
if (x is long)
{
long z = (long)x;
return z >= 0 && y == (ulong)z;
}
if (x is byte)
{
return y == (byte)x;
}
if (x is ushort)
{
return y == (ushort)x;
}
if (x is uint)
{
return y == (uint)x;
}
if (x is ulong)
{
return y == (ulong)x;
}
return false;
}
你能提出一个更好的方法吗?
因为每个整数基本类型都是密封的,引用实现执行的is
操作由JIT编译为对象x
类型的RuntimeTypeHandle
(可能通过一个或两个指针解引用获得)和特定整数类型的句柄(可能实现为内联文字或引用固定内存位置的单个mov
指令)之间的简单相等比较。如果不利用关于方法输入分布的信息,那么改进初始实现将是困难的,如果不是不可能的话。
我没有测试过性能,但它更短:
public static bool NumericalEquals(object x, ulong y)
{
var unsigned = (x as byte?) ?? (x as ushort?) ?? (x as uint?) ?? (x as ulong?);
if (unsigned.HasValue)
{
return (unsigned.Value == y);
}
var signed = (x as sbyte?) ?? (x as short?) ?? (x as int?) ?? (x as long?);
return (signed.HasValue) && (signed.Value >= 0) && ((ulong) signed.Value == y);
}
在最坏的情况下,将有3个强制转换(如object
到sbyte?
到long?
,然后将值转换为ulong
),并且我不谈论Nullable<T>
ab的使用,因此它可能比您的解决方案最慢。
[编辑]
我忘了有一个奖励:没有is
测试。最坏的情况是1个is
和2个cast。所以也许表演可以足够好。
[编辑2]
你也可以考虑一个快速消除测试来提高平均成本:
public static bool NumericalEquals(object x, ulong y)
{
if (x.GetHashCode() != y.GetHashCode()) return false;
...
}
在您原始问题的约束下,我肯定会测试的当前实现的替代方案如下。根据。net框架中表示基本类型的类所使用的Type.GetTypeCodeImpl
的实现,通过在2个虚拟调用的结果上用switch
替换多达5个类型检查(加上一个用于处理枚举),该操作可以证明更快。它利用了TypeCode
枚举包含每个基本整型的成员这一事实。
public static bool NumericalEquals(object x, ulong y)
{
if (x == null)
return false;
Type type = x.GetType();
if (type.IsEnum)
return false;
switch (Type.GetTypeCode(type))
{
case TypeCode.Byte:
return (byte)x == y;
...other cases here
default:
return false;
}
}
可以避免调用IsEnum
,方法是将块包装在try
/catch
中,以处理只有在提供盒装enum作为输入时才会抛出的异常。这可能会提高预期情况下的性能,但代价是对盒装枚举的false
结果较慢。