public void DoSomething(params object[] args)
{
// ...
}
上述签名的问题在于,将传递给该方法的每个值类型都将被隐式装箱,这对我来说是严重的性能问题。
有没有办法在不装箱值类型的情况下清除接受可变数量参数的方法?
谢谢。
public void DoSomething(params object[] args)
{
// ...
}
上述签名的问题在于,将传递给该方法的每个值类型都将被隐式装箱,这对我来说是严重的性能问题。
有没有办法在不装箱值类型的情况下清除接受可变数量参数的方法?
谢谢。
您可以使用泛型:
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) {}
你失去了很好的编译器语法,但是你可以传递任意数量的两个参数。
目前还没有,没有,我还没有看到任何解决已发布的 .NET 4 信息中的问题的内容。
如果这对您来说是一个巨大的性能问题,您可能会考虑几个常见参数列表的重载。
不过,我想知道:这真的是性能问题,还是您过早地优化?
假设您调用此方法的代码知道参数类型。如果是这样,您可以将它们打包成Tuple
.NET 4 中的适当类型,并将其实例(元组是引用类型)传递给对象等方法(因为所有元组都没有共同的基础)。
这里的主要问题是,在没有装箱/拆箱的情况下处理此方法中的参数并不容易,而且很可能,即使没有反射。试着想想必须做些什么来提取,比如说,没有装箱的第 N 个参数。您最终会明白您必须在那里处理字典查找(涉及 CLR 使用的常规Dictionary<K,V>
或内部字典)或装箱。显然,字典查找的成本要高得多。
我写这个是因为实际上我们为非常相似的问题开发了一个解决方案:我们必须能够在没有装箱的情况下使用我们自己的元组进行操作- 主要是为了比较和反序列化它们(元组被我们开发的数据库引擎使用,所以任何性能在我们的案例中,基本操作非常重要)。
但:
我们开发的方法的唯一好处是我们不会用垃圾“淹没” Gen0,因此 Gen0 收集很少发生。由于 Gen0 收集成本与“活动”对象分配的空间及其数量成正比,因此如果其他分配与我们尝试通过这种方式优化的算法的执行混合(或仅在执行期间发生),这会带来明显的优势。
结果:经过优化后,我们的综合测试显示性能提高了 0% 到 200-300%;另一方面,对数据库引擎本身的简单性能测试显示出的改进要小得多(大约 5-10%)。在上面的层上浪费了很多时间(还有一个非常复杂的 ORM),但是......很可能这就是你在实现类似的东西后真正看到的。
简而言之,我建议您专注于其他事情。如果完全清楚这是您的应用程序中的一个主要性能问题,并且没有其他好的方法可以解决它,那么,请继续...否则您只是通过过早的优化从您的客户或您自己的客户那里得到锻炼.
对于完全通用的实现,常见的解决方法是使用流式模式。像这样的东西:
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
但是,这不适用于静态类(扩展方法可以,因为您可以 return this
)。
您的问题与此基本相同:我可以拥有可变数量的泛型参数吗?以不同的方式问道。
或者接受带有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
数组创建开销是否小于装箱开销是您必须自己决定的事情。
在 C# 4.0 中,您可以使用命名(因此是可选的)参数!有关此博客文章的更多信息