Variable number of arguments without boxing the value-types?
Asked Answered
D

5

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

The problem with the above signature is that every value-type that will be passed to that method will be boxed implicitly, and this is serious performance issue for me.

Is there a way to declear a method that accepts variable number of arguments without boxing the value-types?

Thanks.

Didymium answered 11/12, 2009 at 21:28 Comment(0)
S
14

You can use generics:

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

However, this will only allow a single type of ValueType to be specified. If you need to mix or match value types, you'll have to allow boxing to occur, as you're doing now, or provide specific overloads for different numbers of parameters.


Edit: If you need more than one type of parameter, you can use overloads to accomplish this, to some degree.

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

Unfortunately, this requires multiple overloads to exist for your types.

Alternatively, you could pass in arrays directly:

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

You lose the nice compiler syntax, but then you can have any number of both parameters passed.

Slither answered 11/12, 2009 at 21:30 Comment(2)
You're suggesting a solution covering a particular case, but it's clearly stated he needs a generic solution.Uncinate
Well, he actually didn't clearly state that, in his question. I was, on the other hand, very explicit about what exactly this does allow, and what it does not allow.Slither
K
3

Not presently, no, and I haven't seen anything addressing the issue in the .NET 4 info that's been released.

If it's a huge performance problem for you, you might consider several overloads of commonly seen parameter lists.

I wonder, though: is it really a performance problem, or are you prematurely optimizing?

Kishke answered 11/12, 2009 at 21:33 Comment(0)
U
3

Let's assume the code you're calling this method from is aware of argument types. If so, you can pack them into appropriate Tuple type from .NET 4, and pass its instance (Tuple is reference type) to such method as object (since there is no common base for all the Tuples).

The main problem here is that it isn't easy to process the arguments inside this method without boxing / unboxing, and likely, even without reflection. Try to think what must be done to extract, let's say, Nth argument without boxing. You'll end up with understanding you must either deal with dictionary lookup(s) there (involving either regular Dictionary<K,V> or internal dictionaries used by CLR), or with boxing. Obviously, dictionary lookups are much more costly.

I'm writing this because actually we developed a solution for very similar problem: we must be able to operate with our own Tuples without boxing - mainly, to compare and deserialize them (Tuples are used by database engine we develop, so performance of any basic operation is really essential in our case).

But:

  • We end up with pretty complex solution. Take a look e.g. at TupleComparer.
  • Effect of absence of boxing is actually not as good as we expected: each boxing / unboxing operation is replaced by a single array indexing and few virtual method calls, the cost of both ways is almost identical.

The only benefit of approach we developed is that we don't "flood" Gen0 by garbage, so Gen0 collections happen much more rarely. Since Gen0 collection cost is proportional to the space allocated by "live" objects and to their count, this brings noticeable advantage, if other allocations intermix with (or simply happen during) the execution of algorithm we try to optimize by this way.

Results: after this optimization our synthetic tests were showing from 0% to 200-300% performance increase; on the other hand, simple performance test of the database engine itself have shown much less impressive improvement (about 5-10%). A lot of time were wasted at above layers (there is a pretty complex ORM as well), but... Most likely that's what you'll really see after implementing similar stuff.

In short, I advise you to focus on something else. If it will be fully clear this is a major performance problem in your application, and there are no other good ways of resolving it, well, go ahead... Otherwise you're simply steeling from your customer or your own by doing premature optimization.

Uncinate answered 11/12, 2009 at 22:13 Comment(0)
R
-1

For a completely generic implementation, the common workaround is to use a fluent pattern. Something like this:

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

        return this;
    }
}

Now you call:

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

However that doesn't work with static classes (extension methods are ok since you can return this).

Your question is basically the same as this: Can I have a variable number of generic parameters? asked in a different way.

Or accept an array of items with params keyword:

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

    return this;
}

and call:

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

Whether the array creating overhead is less than boxing overhead is something you will have to decide yourself.

Rattlebox answered 3/6, 2013 at 7:13 Comment(0)
R
-2

In C# 4.0 you can use named (and thus optional) parameters! More info on this blog post

Regimentals answered 11/12, 2009 at 22:11 Comment(2)
OMG... It's completely different topic.Uncinate
Hmmm... I'm not yet sure what the required functionality is so maybe I am wrong and maybe I'm not. The phrase "accepts variable number of arguments without boxing the value-types" though I believe can be satisfied by named parameters....Regimentals

© 2022 - 2024 — McMap. All rights reserved.