tldr:
The correct workaround is to tell the compiler to not use the expanded form:
[DataRow(new[] { 1 }, new object[] { new[] { "1" } })]
Excessive analysis:
The answer of Michael Randall is basically correct. Let's dig in by simplifying your example:
using System;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttribute : Attribute {
public MyAttribute(params object[] x){}
}
public class Program
{
[MyAttribute()]
[MyAttribute(new int[0])]
[MyAttribute(new string[0])] // ERROR
[MyAttribute(new object[0])]
[MyAttribute(new string[0], new string[0])]
public static void Main() { }
}
Let's first consider the non error cases.
[MyAttribute()]
There are not enough arguments for the normal form. The constructor is applicable in its expanded form. The compiler compiles this as though you had written:
[MyAttribute(new object[0])]
Next, what about
[MyAttribute(new int[0])]
? Now we must decide if the constructor is applicable in its normal or expanded form. It is not applicable in normal form because int[]
is not convertible to object[]
. It is applicable in expanded form, so this is compiled as though you'd written
[MyAttribute(new object[1] { new int[0] } )]
Now what about
[MyAttribute(new object[0])]
The constructor is applicable in both its normal and expanded form. In that circumstance the normal form wins. The compiler generates the call as written. It does NOT wrap the object array in a second object array.
What about
[MyAttribute(new string[0], new string[0])]
? There are too many arguments for the normal form. The expanded form is used:
[MyAttribute(new object[2] { new string[0], new string[0] })]
That should all be straightforward. What then is wrong with:
[MyAttribute(new string[0])] // ERROR
? Well, first, is it applicable in normal or expanded form? Plainly it is applicable in expanded form. What is not so obvious is that it is also applicable in normal form. int[]
does not implicitly convert to object[]
but string[]
does! This is an unsafe covariant array reference conversion, and it tops my list for "worst C# feature".
Since overload resolution says that this is applicable in both normal and expanded form, normal form wins, and this is compiled as though you'd written
[MyAttribute((object[]) new string[0] )] // ERROR
Let's explore that. If we modify some of our working cases above:
[MyAttribute((object[])new object[0])] // SOMETIMES ERROR!
[MyAttribute((object[])new object[1] { new int[0] } )]
[MyAttribute((object[])new object[2] { new string[0], new string[0] })]
All of these now fail in earlier versions of C# and succeed in the current version.
Apparently the compiler previously allowed no conversion, not even an identity conversion, on the object array. Now it allows identity conversions, but not covariant array conversions.
Casts that can be handled by the compile time constant value analysis are allowed; you can do
[MyAttribute(new int[1] { (int) 100} )]
if you like, because that conversion is removed by the constant analyzer. But the attribute analyzer has no clue what to do with an unexpected cast to object[]
, so it gives an error.
What about the other case you mention? This is the interesting one!
[MyAttribute((object)new string[0])]
Again, let's reason it through. That's applicable only in its expanded form, so this should be compiled as though you'd written
[MyAttribute(new object[1] { (object)new string[0] } )]
But that is legal. To be consistent, either both these forms should be legal, or both should be illegal -- frankly, I don't really care either way -- but it is bizarre that one is legal and the other isn't. Consider reporting a bug. (If this is in fact a bug it is probably my fault. Sorry about that.)
The long and the short of it is: mixing params object[] with array arguments is a recipe for confusion. Try to avoid it. If you are in a situation where you are passing arrays to a params object[] method, call it in its normal form. Make a new object[] { ... }
and put the arguments into the array yourself.