Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the compiler giving an error because of a non constant expression when passing just a string array

When I pass a string array to a test function like this:

[TestCase( new string[] { "1", "2", "3" }, 1 )]
[TestCase( new string[] { "54", "508" }, 1 )]
[TestCase( new string[] { "768" }, 2 )]
public void someTest( string[] someStrings, int someNumber ) {
    //...
}

The compilation works fine.

But, if I remove integer parameter like the follwoing code snippet shows:

[TestCase( new string[] { "1", "2", "3" } )]
[TestCase( new string[] { "54", "508" } )]
[TestCase( new string[] { "768" } )]
public void someTest( string[] someStrings ) {
    //...
}

A compiler error with the message An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type occurs.

I basically get the cause of the error, that an array is not a constant type. But why is the compiler accepting an array, if there is another paramter passed to the test function? It even works, if I put another array inside the TestCase:

[TestCase( new string[] { "1", "2", "3" }, new int[] { 1, 2, 3 } )]
[TestCase( new string[] { "54", "508" }, new int[] { 54, 508 } )]
[TestCase( new string[] { "768" }, new int[] { 768 } )]
public void someTest( string[] someStrings, int[] someNumbers ) {
    //...
}
like image 264
Blackbriar Avatar asked Apr 24 '20 07:04

Blackbriar


2 Answers

Let's simplify this to something where overloading isn't involved, and also remove params:

using System;

[AttributeUsage(AttributeTargets.All)]
class FooAttribute : Attribute
{
    public FooAttribute(object[] args)
    {
    }
}

// Error
[Foo(new string[] { "a", "b" })]
class FooTest1 {}

// Error
[Foo(new[] { "a", "b" })]
class FooTest2 {}

// Error
[Foo(args: new string[] { "a", "b" })]
class FooTest3 {}

// Error
[Foo((object[]) new string[] { "a", "b" })]
class FooTest4 {}

// Works
[Foo(new object[] { "a", "b" })]
class FooTest5 {}

// Works
[Foo(new[] { (object) "a", "b" })]
class FooTest6 {}

Basically the compiler is unwilling to supply a string[] for a object[] parameter in an attribute, even though that would normally be fine.

I believe this is a compiler bug, having checked the spec - but I wouldn't like to say for certain. The expression new string[] { "a", "b" } does count as an attribute-argument-expression in spec terms - and if you change the parameter type to string[] it works fine. So it's the application of that argument type to the parameter that's the problem. The spec also says that the attribute parameter and argument are "are constrained by the same rules as simple assignment" - but that would be fine in this case. So I can't see anything in the spec that should disallow this.

like image 185
Jon Skeet Avatar answered Nov 11 '22 16:11

Jon Skeet


Looks like an overloading issue.

TestCaseAttribute has the following constructors:

TestCaseAttribute(object arg);                             // A
TestCaseAttribute(object arg1, object arg2);               // B
TestCaseAttribute(object arg1, object agr2, object arg3);  // C
TestCaseAttribute(params object[] arguments);              // D

Your first example matches (B) as does your third.

You want the second (failing one) to match (A) but in fact it's matching (D)

Try casting your array to an object

[TestCase( (object) new string[] { "1", "2", "3" } )]

or use named parameter passing

[TestCase( arg: new string[] { "1", "2", "3" } )]

The rules of attributes allow one-dimensional array to be passed, but ont two-dimensional arrays (as described here), so (D) is not allowed with an array.

like image 1
Jasper Kent Avatar answered Nov 11 '22 16:11

Jasper Kent