How to Pass a Single Object[] to a Params Object[]

How to pass a single object[] to a params object[]

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 solution though, I'll agree.

How to pass parameters as a single object?

If all the parameter values required in the method are of same type(let it be string) then you can pass the parameter as a Dictionary like the following:

private void myMethod(Dictionary<string,string> paramDictionary)
{
// do things
}

So that you can call the method like this:

Dictionary<string,string> paramDictionary = new Dictionary<string,string>();
paramDictionary.Add("b", "b_value");
paramDictionary.Add("c", "c_value");
myMethod(paramDictionary);

Why does params object[] need passing as new object[] { parameters } for Com Method?

I've come to realise that the original passParameters object in the example contains two elements, as we know:

passParameters[0] = "test1";
passParameters[1] = "test2";

However, when the array is passed to the Com method, it requires being passed as so:

new object[] { passParameters })

My understanding of this is, that it creates a new object array which has put the existing 'passParameters' array into its first element [0] (effectively creating a new 2d array):

passParameters[0][0] = "test1";
passParameters[0][1] = "test2";

The method MethodInfo class in my question, required a single object passed to the Invoke method, which meant that the original 'passParameters' object with its two elements had too many arguments. By passing it inside a new object, this passes all of the elements as a single object, but containing an array of those parameters.

Passing single object vs. passing multiple parameters

Method A (naked parameters) always has the advantages that

  • it requires the method author to type less, since they don't have to implement a Parameter Object,
  • it requires the method caller to type less, since they don't have to instantiate a Parameter Object
  • it performs better, since no Parameter Object has to be constructed and garbage collected
  • the reader can see what the individual parameters are from the method signature alone (but this is a double-edged sword; see below)

Method B (Parameter Object) has advantages when

  • the parameters have domain meaning as a group, so the Parameter Object can be given a name that explains that meaning, saving the reader from having to read and understand each member of the group and how they relate
  • the parameter list is used in more than one method, so using the Parameter Object in each reduces duplication
  • the values in the parameter list are passed around among multiple methods as a group, which is easier when they can be passed as a single Parameter Object
  • some combinations of values are invalid; the Parameter Object can prevent those combinations
  • some values are optional, which can be provided by the Parameter Object instead of (depending on your language) default parameter values or overloaded methods
  • there is more than one parameter of the same type, making value-swapping errors more likely (although a Parameter Object is not better in this case if it
    has a constructor with the same parameter list as the method)

That the Parameter Object introduces a new dependency on which caller and callee depend is not much of a disadvantage, since it is a simple class with no dependencies of its own.

So, Parameter Object is

  • almost never worthwhile for a single parameter, sometimes worthwhile for a two-parameter method (e.g. Point is usually better than x, y) and sometimes not, and increasingly helpful with three and more parameters
  • increasingly helpful when more methods use the same parameter list

C# params object[] strange behavior

Nice find!

Is this undefined behavior?

No. This is by-design behaviour. Weird design, but by design.

Does that depend on the .Net framework version / Mono version?

No. All versions of C# have this behaviour.

This is a consequence of the collision of some interesting rules of C#.

The first relevant rule is: a method with a params array may be called in either "normal" or "expanded" form. Normal form is as if there was no "params". Expanded form takes the params and bundles them up into an array that is automatically generated. If both forms are applicable then normal form wins over expanded form.

Now, that perhaps seems sensible; if you have an array of objects in hand, odds are good that you want to pass the array of objects, not an array that contains an array of objects.

The second relevant rule is that C# allows unsafe array covariance when the element type is a reference type. That is, an array of strings may be converted to an array of objects implicitly. You'll note that this has two implications. First, it means that when you have an array of objects, it might actually be an array of strings, so putting, say, a turtle into that array of objects might cause a type error. This is very surprising! You expect that every array of objects can take any object, but that is not true in C#. Some arrays of objects are lying.

The second implication is: since putting that turtle into what is really an array of strings must throw, it means that every time you put something into an array of base type, the runtime must verify that the types check. So array writes are extra expensive in C# on every write, so that the vanishingly small minority of bad writes can be caught.

This is a mess, and this is why unsafe array covariance tops my list of unfortunate C# features.

The combination of these two rules explains your observations. The array of strings is convertible to an array of objects, and therefore the method is applicable in normal form.

For the array of ints, well, covariance does not apply to value types. So the array of ints is not convertible to an array of objects, so the method is not applicable in its normal form. But an array of ints is an object, so it is applicable in expanded form.

See also:

Why does params behave like this?

Your question is arguably a duplicate of:

Is there a way to distingish myFunc(1, 2, 3) from myFunc(new int[] { 1, 2, 3 })?

Elegantly passing lists and objects as params

You could create a class with implict conversions to wrap a single element and a list:

public class ParamsWrapper<T> : IEnumerable<T>
{
private readonly IEnumerable<T> seq;

public ParamsWrapper(IEnumerable<T> seq)
{
this.seq = seq;
}

public static implicit operator ParamsWrapper<T>(T instance)
{
return new ParamsWrapper<T>(new[] { instance });
}

public static implicit operator ParamsWrapper<T>(List<T> seq)
{
return new ParamsWrapper<T>(seq);
}

public IEnumerator<T> GetEnumerator()
{
return this.seq.GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}

then you can change your DoStuff method to:

private static void DoStuff(params ParamsWrapper<Foo>[] foos)
{
Foo[] all = foos.SelectMany(f => f).ToArray();
//
}

C# - pass on expanded params array to another function

You make a new array, copy the existing parameters into it, add more things and call the other method with the larger array

These are, when all is said and done, just arrays. params just means the compiler lets you specify arguments individually and it will turn it into an array for you

You write this:

MyFuncWithParamsObjectArg(1,2,3,4,5);

The compiler conceptually changes it to:

MyFuncWithParamsObjectArg(new object [] {1,2,3,4,5} );

Here's a demo program:

public static void Main()
{
A("z", "y");

}

static void A(params object[] a){
Console.WriteLine("In A()");

foreach(object o in a)
Console.WriteLine(o);

object[] oo = new object[a.Length + 2];

for(int i = 0; i < a.Length; i++)
oo[i] = a[i];

oo[a.Length] = "new";
oo[a.Length + 1] = "new2";

B(oo);
}

static void B(params object[] b){
Console.WriteLine("In B()");

foreach(object o in b)
Console.WriteLine(o);
}

Prints out this:

In A()
z
y
In B()
z
y
new
new2

You can see that B was called with an array 2 longer than a, and added two more elements

Note, you can't "expand" an array, regardless of whether it's params or not. You have to make a new array, copy the stuff over, and go from there

If you're looking to wrap it up so that you're not flattening the first array, but instead making another params array that has the first array as one of its elements:

static void A(params object[] a){
Console.WriteLine("In A()");

foreach(object o in a)
Console.WriteLine(o);

B(a, "new1", "new2");
}

B will now get an array that looks like:

object[] {
object[] { "z", "y" },
"new1",
"new2"
}

As noted; there is no magic - the compiler looks at what you provided and if the arguments match "called with a single dimension array" then it calls the function with the array, otherwise it scoops up the various arguments, turns them into an array, and calls it with that array

B(a);                                  //a is already an object array, it is passed in verbatim
B(a, "new1", "new2"); //a new object array is created from these 3 objects
B(new object[] {a, "new1", "new2"} ); //manually doing what the compiler does for you above

How to pass object[] to Activator.CreateInstance(type, params object[])

Cast it to object:

Activator.CreateInstance(yourType, (object) yourArray);


Related Topics



Leave a reply



Submit