【问题标题】:Casting generic parameter to and from integer将泛型参数转换为整数和从整数转换为整数
【发布时间】:2013-08-12 21:33:17
【问题描述】:

我想编写泛型类,该类旨在与byteushort 等内置类型一起使用。在内部计算中,我需要将泛型类型转换为整数并返回泛型类型。我找到了编译此类代码的方法,例如:

class Test<T> where T : struct, IConvertible
{
    public static T TestFunction(T x)
    {
        int n = Convert.ToInt32(x);
        T result = (T)Convert.ChangeType(n, typeof(T));
        return result;
    }
}

我认为,如果在计算循环中使用此类转换,可能会显着降低性能。有没有更好的方法来进行这些转换?

【问题讨论】:

  • 您对此性能的基准是什么,否则要改进的人是什么?更不用说你怎么知道这甚至是一个真正的问题。
  • @GrantThomas - Convert.ToInt32Convert.ChangeType 都接受 Object 参数。这意味着,此代码需要装箱。我可以换个方式问这个问题:是否可以避免拳击?
  • 您可以,甚至可以使其更快,但这仍然不会成为问题。
  • .net 无论如何都会优化 32 位整数。见link

标签: c# generics casting


【解决方案1】:

intT 的转换有点棘手。我想你可以在这里使用Expression 类。

Test&lt;T&gt; 类应该是这样的:

class Test<T> where T : struct, IConvertible
{
    private static Func<int, T> _getInt;

    static Test()
    {
        var param = Expression.Parameter(typeof(int), "x");
        UnaryExpression body = Expression.Convert(param, typeof(T));
        _getInt = Expression.Lambda<Func<int, T>>(body, param).Compile();
    }

    public static T TestFunction(T x)
    {
        int n = Convert.ToInt32(x);
        T result = _getInt(n);
        return result;
    }
}

它在静态构造函数中为您准备_getInt = x =&gt; (T)x,稍后使用它,将int 转换为T

【讨论】:

  • 谢谢,使用预编译的 lambda 提高了函数性能。
  • 我也在另一个方向(T 到 int)上匹配了预编译的 lambda,并在直方图计算上获得了显着的性能提升。
  • 这减少了超过 50% 的执行时间!直方图函数将大部分时间浪费在装箱上,这是 Convert.ToInt32 和 Convert.ChangeType 函数的一部分。
  • 这真的很有趣!稍后我会自己进行一些测试!您的测试套件中的T 是什么类型?因为Convert.ToInt32 有一个以byte 为参数的重载,所以那里应该没有装箱。
  • 好的,我明白这里发生了什么。其实Convert.ToInt32(byte)byte.ToInt32好多了!
【解决方案2】:

经过一番思考,我很高兴感谢这个问题和一些答案,我解决了我的一个老问题:在泛型 T 上使用操作:

首先是 Cast 的示例(根据 OP 的要求)

public static class Cast<T, U>
{
    public static readonly Func<T, U> Do;

    static Cast()
    {
        var par1 = Expression.Parameter(typeof(T));

        Do = Expression.Lambda<Func<T, U>>(Expression.Convert(par1, typeof(U)), par1).Compile();
    }
}

然后是一个乘法示例:

public static class Multiply<T>
{
    public static readonly Func<T, T, T> Do;

    static Multiply()
    {
        var par1 = Expression.Parameter(typeof(T));
        var par2 = Expression.Parameter(typeof(T));

        Do = Expression.Lambda<Func<T, T, T>>(Expression.Multiply(par1, par2), par1, par2).Compile();
    }
}

使用很简单:

int x = Conv<T, int>.Do(someTValue);

最后创建了一个静态类,其中包含一个名为Do 的只读静态属性字段,它是一个“指向”使用表达式树构建的操作的委托。

乘法类似:

T res = Multiply<T, T>.Do(someTValue1, someTValue2);

在一般情况下,乘法比直接乘法慢 3 倍(在发布模式下,无需调试)。

从乘法开始,显然做其他操作很简单

(有趣的是,我非常了解 Expression 树,但我从未想过使用静态类作为包含各种类型的“字典”。我总是做类似 Dictionary&lt;Type, Delegate&gt; 的事情,而不是让.NET 通过泛型类特化“处理”Dictionary。)

【讨论】:

  • 非常有趣。动态击败拳击?我现在正在测试这个,谢谢。
  • @AlexFarber 我没有做过任何纯拳击方法。您不能纯粹拆箱到另一种类型。如果 T is longT x(int)(object)x 是 InvalidCastException。试试int res = (int)(object)5L
  • 真的很有趣。令人惊讶的是,使用 dynamic 提供了与使用 Convert 函数相同的性能。使用表达式可以获得更好的性能,谢谢。
  • 我希望接受您的回答,尤其是在奇怪的反对票之后,但您将其替换为您自己问题的答案 :) 我赞成并接受另一个答案。希望本次讨论对 SO 用户有所帮助。
猜你喜欢
  • 2011-10-05
  • 2019-08-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 2016-02-04
相关资源
最近更新 更多