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;
}
你能提出一个更好的方法吗?

c#中检查盒装原语整型数值的最快方法

因为每个整数基本类型都是密封的,引用实现执行的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个强制转换(如objectsbyte?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结果较慢。