可变数量的参数没有装箱值类型?

时间:2009-12-11 21:28:19

标签: c# .net performance boxing value-type

public void DoSomething(params object[] args)
{
    // ...
}

上述签名的问题在于,每个传递给该方法的值类型都会被隐式装箱,这对我来说是严重的性能问题。

有没有办法去掉一个接受可变数量的参数的方法,而不需要装入值类型?

感谢。

5 个答案:

答案 0 :(得分:12)

您可以使用泛型:

public void DoSomething<T>(params T[] args)
{
}

但是,这只允许指定单一类型的ValueType。如果你需要混合或匹配值类型,你必须允许装箱,就像现在一样,或者为不同数量的参数提供特定的重载。


编辑:如果您需要多种类型的参数,可以在某种程度上使用重载来实现此目的。

public void DoSomething<T,U>(T arg1, params U[] args) {}
public void DoSomething<T,U>(T arg1, T arg2, params U[] args) {}

不幸的是,这需要为您的类型存在多个重载。

或者,您可以直接传入数组:

public void DoSomething<T,U>(T[] args1, U[] args2) {}

你失去了很好的编译器语法,但是你可以传递任意数量的两个参数。

答案 1 :(得分:3)

目前还没有,并且我还没有看到任何解决已发布的.NET 4信息中的问题。

如果这对您来说是一个巨大的性能问题,您可能会考虑常见参数列表的几个重载。

我想知道:它是真的性能问题,还是你过早地优化了?

答案 2 :(得分:3)

让我们假设您调用此方法的代码知道参数类型。如果是这样,您可以将它们从.NET 4打包到适当的Tuple类型中,并将其实例(元组是引用类型)传递给对象这样的方法(因为所有元组都没有公共基础)。

这里的主要问题是在没有装箱/拆箱的情况下处理此方法内的参数并不容易,甚至可能没有反射。尝试思考必须要做什么来提取,比方说,没有拳击的第N个参数。你最终会理解你必须处理那里的字典查找(涉及CLR使用的常规Dictionary<K,V>internal dictionaries),或者装箱。显然,字典查找的成本要高得多。

我写这篇文章是因为实际上我们为非常类似的问题开发了一个解决方案:我们必须能够在没有装箱的情况下使用our own Tuples进行操作 - 主要是对它们进行比较和反序列化(元组由我们开发的数据库引擎使用)因此,在我们的案例中,任何基本操作的执行都是必不可少的)。

可是:

  • 我们最终得到了非常复杂的解决方案。看看,例如在TupleComparer
  • 没有拳击的效果实际上并不像我们预期的那么好:每个装箱/拆箱操作都被单个数组索引和几个虚方法调用所取代,两种方式的成本几乎相同。

我们开发的方法的唯一好处是我们不会通过垃圾“泛滥”Gen0,因此Gen0集合很少发生。由于Gen0收集成本与“实时”对象分配的空间及其计数成比例,这带来了明显的优势,如果其他分配与算法执行混合(或仅仅发生),我们尝试通过这种方式优化。

结果:在此优化后,我们的综合测试显示性能提升0%至200-300%;另一方面,数据库引擎本身的简单性能测试显示出不太令人印象深刻的改进(约5-10%)。很多时间浪费在上面的层次(也有一个非常复杂的ORM),但是...很可能这是你在实现类似的东西之后真正看到的。

简而言之,我建议你专注于其他事情。如果它完全清楚这是你的应用程序中的一个主要性能问题,并且没有其他好方法可以解决它,那么,继续... 否则你只是简单地从你的客户或你自己的做过早优化

答案 3 :(得分:0)

对于完全通用的实现,常见的解决方法是使用流畅的模式。像这样:

public class ClassThatDoes
{
    public ClassThatDoes DoSomething<T>(T arg) where T : struct
    {
        // process

        return this;
    }
}

现在你打电话:

classThatDoes.DoSomething(1).DoSomething(1m).DoSomething(DateTime.Now)//and so on

但是这对静态类不起作用(扩展方法没问题,因为你可以返回this)。

你的问题与此基本相同:Can I have a variable number of generic parameters?以不同的方式提问。

或接受包含params关键字的项目数组:

public ClassThatDoes DoSomething<T>(params T[] arg) where T : struct
{
    // process

    return this;
}

并致电:

classThatDoes.DoSomething(1, 2, 3)
             .DoSomething(1m, 2m, 3m)
             .DoSomething(DateTime.Now) //etc

创建开销的数组是否小于拳击开销是你必须自己决定的。

答案 4 :(得分:-1)

在C#4.0中,您可以使用命名(因此可选)参数!有关this blog post

的更多信息