tags:

views:

5156

answers:

6

I have a method which takes params object[] such as:

void Foo(params object[] items)
{
    Console.WriteLine(items[0]);
}

When I pass two object arrays to this method, it works fine:

Foo(new object[]{ (object)"1", (object)"2" }, new object[]{ (object)"3", (object)"4" } );
// Output: System.Object[]

But when I pass a single object[], it does not take my object[] as the first param, instead it takes its all elements like I wanted to pass them one by one:

Foo(new object[]{ (object)"1", (object)"2" });
// Output: 1, expected: System.Object[]

How do I pass a single object[] as a first argument to a params array?

+2  A: 

You need to encapsulate it into another object[] array, like this:

Foo(new Object[] { new object[]{ (object)"1", (object)"2" }});
Lasse V. Karlsen
A: 

One option is you can wrap it into another array:

Foo(new object[]{ new object[]{ (object)"1", (object)"2" } });

Kind of ugly, but since each item is an array, you can't just cast it to make the problem go away... such as if it were Foo(params object items), then you could just do:

Foo((object) new object[]{ (object)"1", (object)"2" });

Alternatively, you could try defining another overloaded instance of Foo which takes just a single array:

void Foo(object[] item)
{
    // Somehow don't duplicate Foo(object[]) and
    // Foo(params object[]) without making an infinite
    // recursive call... maybe something like
    // FooImpl(params object[] items) and then this
    // could invoke it via:
    // FooImpl(new object[] { item });
}
Mike Stone
+12  A: 

A simple typecast will ensure the compiler knows what you mean in this case.

Foo((object)new object[]{ (object)"1", (object)"2" }));

As an array is a subtype of object, this all works out. Bit of an odd semantic though, I'll agree.

Edit: Woops, typoed my example code.

Adam Wright
A: 

@Adam Wright

I was thinking that too, except then I realized the Foo paramter is "params object[] items", so casting as an object won't work, and I think casting as an object array would probably have the same effect as without the cast (though I haven't tried it, so I don't know).

Update: @Adam Wright: Hmmm... maybe I'm misremembering C# semantics... it's been a bit since I've used C#, so I guess it might be best to ignore me ;-)

Mike Stone
A: 

@Mike Stone, I figured that would be the case as well, but tests give the desired result. Which is why I said it was an odd semantic.

My conclusion is that the generated IL function code is from the static type of the parameter. Object[] passed to a params type? Destructure into individual parameters. Anything else? Pass as a parameter. Changing the static type to Object via casting thus changes the call semantics.

Adam Wright
+6  A: 

The params parameter modifer gives callers a shortcut syntax for passing multiple arguments to a method. There are two ways to call a method with a params parameter:

1) Calling with an array of the parameter type, in which case the params keyword has no effect and the array is passed directly to the method:

object[] array = new[] { "1", "2" };

// Foo receives the 'array' argument directly.
Foo( array );

2) Or, calling with an extended list of arguments, in which case the compiler will automatically wrap the list of arguments in a temporary array and pass that to the method:

// Foo receives a temporary array containing the list of arguments.
Foo( "1", "2" );

// This is equivalent to:
object[] temp = new[] { "1", "2" );
Foo( temp );


In order to pass in an object array to a method with a "params object[]" parameter, you can either:

1) Create a wrapper array manually and pass that directly to the method, as mentioned by lassevk:

Foo( new object[] { array } );  // Equivalent to calling convention 1.

2) Or, cast the argument to object, as mentioned by Adam, in which case the compiler will create the wrapper array for you:

Foo( (object)array );  // Equivalent to calling convention 2.


However, if the goal of the method is to process multiple object arrays, it may be easier to declare it with an explicit "params object[][]" parameter. This would allow you to pass multiple arrays as arguments:

void Foo( params object[][] arrays ) {
  foreach( object[] array in arrays ) {
    // process array
  }
}

...
Foo( new[] { "1", "2" }, new[] { "3", "4" } );

// Equivalent to:
object[][] arrays = new[] {
  new[] { "1", "2" },
  new[] { "3", "4" }
};
Foo( arrays );
Emperor XLII