Case
This morning I refactored some Logging method and needed to change a method's 'params' parameter in a normal array. Consequently, the call to the method had to change with an array parameter. I'd like the method call to change as less as possible, since it's a heavily used utility method.
I assumed I should be able to use the collection initializer to call the method, but it gave me a compile-error. See the second call in the example below. The third call would be fine too, but also results in an error.
Example
void Main()
{
// This works.
object[] t1 = { 1, "A", 2d };
Test(t1);
// This does not work. Syntax error: Invalid expression term '{'.
Test({1, "A", 2d });
// This does not work. Syntax error: No best type found for implicitly-typed array.
Test(new[] { 1, "A", 2d });
// This works.
Test(new object[] { 1, "A", 2d });
}
void Test(object[] test)
{
Console.WriteLine(test);
}
Question
Is there any way to call Test() without initializing an array first?
The problem is that C# is trying infer the type of the array. However, you provided values of different types and thus C# cannot infer the type. Either ensures that all you values are of the same type, or explicitly state the type when you initialize the array
var first = new []{"string", "string2", "string3"};
var second = new object[]{0.0, 0, "string"};
Once you stop using params there is no way back. You will be forced to initialize an array.
Alternative continue using params:
public void Test([CallerMemberName]string callerMemberName = null, params object[] test2){}
Related
I am getting the error
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
Notice the screenshot below:
Notice that if I use the DataRow attribute with one or three parameters, I don't get a compile error. But if I use two parameters and the second parameter is an array of strings, then I do get a compile error.
The signatures for DataRowAttribute are public DataRowAttribute (object data1); and public DataRowAttribute (object data1, params object[] moreData);
The first one gives me no problem, but the second one seems to be getting confused.
I considered that maybe the params object[] could be causing some confusion.
Maybe it couldn't determine whether I meant [DataRow(new[] { 1 }, new[] { "1" })] or [DataRow(new[] { 1 }, "1")]
To resolve that, I tried to cast the second attribute to object ([DataRow(new[] { 1 }, (object)new[] { "1" })]), but the error didn't go away and it warned me that the cast was redundant. I also tried specifying the types of the array explicitly, but that also did not help.
I could just add a third dummy parameter, even null seems to fix this, but that's just a workaround. What's the correct way to do this?
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.
Assuming your constructor is
public Foo(params object[] vals) { }
Then i think you are running up against some overlooked and non obvious compiler Dark Magic.
For example, obviously the below will work
[Foo(new object[] { "abc", "def" },new object[] { "abc", "def" })]
[Foo(new string[] { "abc", "def" },new string[] { "abc", "def" })]
This also works for me
[Foo(new [] { 2 }, new [] { "abc"})]
[Foo(new [] { 1 }, new [] { "a"})]
However this does not
[Foo(new [] { "a" })]
[Foo(new [] { "aaa"})]
[Foo(new string[] { "aaa" })]
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
I think the key take-home peice of information here 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.
As an example
PrintLength(new string[] {"hello"}); // normal form
PrintLength("hello"); // expanded form, translated into normal form by compiler.
When given a call that is applicable in both forms, the compiler always chooses the normal form over the expanded form.
However i think this gets even messier again with object[] and even attributes.
I'm not going to pretend i know exactly what the CLR is doing (and there are many more qualified people that may answer). However for reference, take a look at the CLR SO wizard Eric Lippert's similar answers for a more detailed illumination of what might be going on
C# params object[] strange behavior
Why does params behave like this?
Is there a way to distingish myFunc(1, 2, 3) from myFunc(new int[] { 1, 2, 3 })?
Having the code below, why is the variable declaration considered as correct syntax, but not also the method call?
public static void Func(string[] p)
{
}
public static void Test()
{
string[] a = { "x", "y", "z" };
Func({"x", "y", "z"});
}
It looks like everybody else is focusing on the workaround (which is simply to specify that you need a new [] { "some", "strings" }. The why is a little less obvious though.
The former is valid because the compiler knows to use your initialization syntax to create an Array (because you've defined it as such).
The later would have some issues. It may seem trivial in this case because the compiler should, theoretically, be able to figure out that you need a string[]. Think about cases where you have a Func<IEnumerable<string>> though. What type gets generated in that case? Would the compiler take a wild-ass guess? Always use an Array (even though there might be a better fit)? It could be one of a number of possibilities.
I'm guessing that's the reason that the language specification doesn't allow for passing things this way.
You need to pass in a value as the argument. {"x", "y", "z"} is not a value. It can be used as short-hand for initializing a variable.
Note that both of these are valid:
List<string> a = new List<string>() {"x", "y", "z"};
string[] b = new string[] {"x", "y", "z"};
And the full version of what it represents:
List<string> a = new List<string>();
a.Add("x");
a.Add("y");
a.Add("z");
So you need to make an object (new)
new [] {"x", "y", "z"}
Or make an object beforehand and pass that in.
As for why this is like this, you need to pass in a value, not a helper for array initialization.
You can initialize the object directly on the inside of the method call, but do not think a good practice.
where you used:
Func ({"x", "y", "z"});
The above form you tried to pass an object not instantiated, ie it does not already exist on your "context". The correct is you initialize it to be generated in a reference memory to this value and thus the "Func" method you can use it.
In the case of variable declaration:
string[] a = {"x", "y", "z"};
In this case your compiler is reading:
string[] a = new string[] {"x", "y", "z"};
It does this interpretation alone, without requiring you to do object initialization explicitly.
So my answer is best for this situation you must first create the object then pass it as a parameter to the method:
string[] a = {"x", "y", "z"};
Func (s);
A question to all of you C# wizards. I have a method, call it myFunc, and it takes variable length/type argument lists. The argument signature of myFunc itself is myFunc(params object[] args) and I use reflection on the lists (think of this a bit like printf, for example).
I want to treat myFunc(1, 2, 3) differently from myFunc(new int[] { 1, 2, 3 }). That is, within the body of myFunc, I would like to enumerate the types of my arguments, and would like to end up with { int, int, int} rather than int[]. Right now I get the latter: in effect, I can't distinguish the two cases, and they both come in as int[].
I had wished the former would show up as obs[].Length=3, with obs[0]=1, etc.
And I had expected the latter to show up as obs[].Length=1, with obs[0]={ int[3] }
Can this be done, or am I asking the impossible?
Well this will do it:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("First call");
Foo(1, 2, 3);
Console.WriteLine("Second call");
Foo(new int[] { 1, 2, 3 });
}
static void Foo(params object[] values)
{
foreach (object x in values)
{
Console.WriteLine(x.GetType().Name);
}
}
}
Alternatively, if you use DynamicObject you can use dynamic typing to achieve a similar result:
using System;
using System.Dynamic;
class Program
{
static void Main(string[] args)
{
dynamic d = new ArgumentDumper();
Console.WriteLine("First call");
d.Foo(1, 2, 3);
Console.WriteLine("Second call");
d.Bar(new int[] { 1, 2, 3 });
}
}
class ArgumentDumper : DynamicObject
{
public override bool TryInvokeMember
(InvokeMemberBinder binder,
Object[] args,
out Object result)
{
result = null;
foreach (object x in args)
{
Console.WriteLine(x.GetType().Name);
}
return true;
}
}
Output of both programs:
First call
Int32
Int32
Int32
Second call
Int32[]
Now given the output above, it's not clear where your question has really come from... although if you'd given Foo("1", "2", "3") vs Foo(new string[] { "1", "2", "3" }) then that would be a different matter - because string[] is compatible with object[], but int[] isn't. If that's the real situation which has been giving you problems, then look at the dynamic version - which will work in both cases.
OK, so let's say that we abandon the other question where you incorrectly believe that any of this is a compiler bug and actually address your real question.
First off, let's try to state the real question. Here's my shot at it:
The preamble:
A "variadic" method is a method which takes an unspecified-ahead-of-time number of parameters.
The standard way to implement variadic methods in C# is:
void M(T1 t1, T2 t2, params P[] p)
that is, zero or more required parameters followed by an array marked as "params".
When calling such a method, the method is either applicable in its normal form (without params) or its expanded form (with params). That is, a call to
void M(params object[] x){}
of the form
M(1, 2, 3)
is generated as
M(new object[] { 1, 2, 3 });
because it is applicable only in its expanded form. But a call
M(new object[] { 4, 5, 6 });
is generated as
M(new object[] { 4, 5, 6 });
and not
M(new object[] { new object[] { 4, 5, 6 } });
because it is applicable in its normal form.
C# supports unsafe array covariance on arrays of reference type elements. That is, a string[] may be implicitly converted to object[] even though attempting to change the first element of such an array to a non-string will produce a runtime error.
The question:
I wish to make a call of the form:
M(new string[] { "hello" });
and have this act like the method was applicable only in expanded form:
M(new object[] { new string[] { "hello" }});
and not the normal form:
M((object[])(new string[] { "hello" }));
Is there a way in C# to implement variadic methods that does not fall victim to the combination of unsafe array covariance and methods being applicable preferentially in their normal form?
The Answer
Yes, there is a way, but you're not going to like it. You are better off making the method non-variadic if you intend to be passing single arrays to it.
The Microsoft implementation of C# supports an undocumented extension that allows for C-style variadic methods that do not use params arrays. This mechanism is not intended for general use and is included only for the CLR team and others authoring interop libraries so that they can write interop code that bridges between C# and languages that expect C-style variadic methods. I strongly recommend against attempting to do so yourself.
The mechanism for doing so involves using the undocumented __arglist keyword. A basic sketch is:
public static void M(__arglist)
{
var argumentIterator = new ArgIterator(__arglist);
object argument = TypedReference.ToObject(argumentIterator.GetNextArg());
You can use the methods of the argument iterator to walk over the argument structure and obtain all the arguments. And you can use the super-magical typed reference object to obtain the types of the arguments. It is even possible using this technique to pass references to variables as arguments, but again I do not recommend doing so.
What is particularly awful about this technique is that the caller is required to then say:
M(__arglist(new string[] { "hello" }));
which frankly looks pretty gross in C#. Now you see why you are better off simply abandoning variadic methods entirely; just make the caller pass an array and be done with it.
Again, my advice is (1) under no circumstances should you attempt to use these undocumented extensions to the C# language that are intended as conveniences for the CLR implementation team and interop library authors, and (2) you should simply abandon variadic methods; they do not appear to be a good match for your problem space. Do not fight against the tool; choose a different tool.
Yes, you can, checking the params length and checking the argument type, see the following working code sample:
class Program
{
static void Main(string[] args)
{
myFunc(1, 2, 3);
myFunc(new int[] { 1, 2, 3 });
}
static void myFunc(params object[] args)
{
if (args.Length == 1 && (args[0] is int[]))
{
// called using myFunc(new int[] { 1, 2, 3 });
}
else
{
//called using myFunc(1, 2, 3), or other
}
}
}
You can achieve something like this by breaking the first element out of the list and providing an extra overload, for example:
class Foo
{
public int Sum()
{
// the trivial case
return 0;
}
public int Sum(int i)
{
// the singleton case
return i;
}
public int Sum(int i, params int[] others)
{
// e.g. Sum(1, 2, 3, 4)
return i + Sum(others);
}
public int Sum(int[] items)
{
// e.g. Sum(new int[] { 1, 2, 3, 4 });
int i = 0;
foreach(int q in items)
i += q;
return i;
}
}
This is not possible in C#. C# will replace your first call by your second call at compile time.
One possibility is to create an overload without params and make it a ref parameter. This probably won't make sense. If you want to change behavior based on the input, perhaps give the second myFunc another name.
Update
I understand your issue now. What you want is not possible. If the only argument is anything that can resolve to object[] it's impossible to distinguish from this.
You need an alternative solution, maybe have a dictionary or array created by the caller to build up the parameters.
Turns out that there was a real issue, and it comes down to the way that C# does type inference. See the discussion on this other thread
List<string> a = new List<string>() { "a", "b", "c" };
List<string> b = new List<string>() { "a", "b", "c", "d", "e", "f" };
b.RemoveAll(a.Contains);
If you loop through b it will now only contain d e and f. Can anyone expand out whats actually happening, because currently it doesnt make any sense at all.
Edit: I was more talking about the use of predicates. How does know how to pass what into where?
b.RemoveAll(<function that takes a string and returns true if we want to remove it>)
no lambda expression required.
perhaps you'd like for the line to read
b.RemoveAll(x => a.Contains(x))
however, the function x=> a.Contains(x) is simply a function that takes a string and returns a bool indicating whether a contains x. a.Contains is already a function that does that.
The { } Syntax is a collection initializer. The code is equivalent to
List<string> a = new List<string>();
a.Add("a");
a.Add("b");
a.Add("c");
List<string> b = new List<string>();
b.Add("a");
b.Add("b");
b.Add("c");
b.Add("d");
b.Add("e");
b.Add("f");
b.RemoveAll is a function that calls another function and passes in a string. It's like this:
foreach(string s in b) {
if(FunctionToCall(s) == true) b.Remove(s);
}
a.Contains is a function that takes a string and returns a bool. So the code can be changed to:
foreach(string s in b) {
if(a.Contains(s)) b.Remove(s);
}
Note that in this Lambda-Syntax, you are passing the "a.Contains" function - not the result of the function! It's like a function pointer. RemoveAll expects to take a function in the form of "bool FunctionName(string input)".
Edit: Do you know what delegates are? They are a bit like function pointers: A delegate specifies a signature ("Takes 2 strings, returns an int"), and then you can use it like a variable. If you don't know about delegates, read Karl Seguins article.
Some delegates are needed extremely often, so the .net Framework developers added three types of extremely common delegates:
Predicate: A delegate that takes a T and returns a bool.
Action: A delegate that takes 1 to 4 parameters and returns void
Function: A delegate that takes 0 to 4 parameters and returns a T
(Shamelessly copied from Jon Skeet's Answer here)
So predicate is just the name given for a delegate, so that you don't have to specify it yourself.
If you have ANY function in your assembly with the signature
"bool YourFunction(string something)", it is a Predicate<string> and can be passed into any other function that takes one:
public bool SomeFunctionUsedAsPredicate(string someInput)
{
// Perform some very specific functionality, i.e. calling a web
// service to look up stuff in a database and decide if someInput is good
return true;
}
// This Function is very generic - it does not know how to check if someInput
// is good, but it knows what to do once it has found out if someInput is good or not
public string SomeVeryGenericFunction(string someInput, Predicate<string> someDelegate)
{
if (someDelegate.Invoke(someInput) == true)
{
return "Yup, that's true!";
}
else
{
return "Nope, that was false!";
}
}
public void YourCallingFunction()
{
string result = SomeVeryGenericFunction("bla", SomeFunctionUsedAsPredicate);
}
The whole point is separation of concerns (see the comment in SomeGenericFunction) and also to have very generic functions. Look at my generic, extensible string encoding function. This uses the Func rather than the Predicate delegate, but the purpose is the same.
look at it like this:
foreach(string s in b)
{
if(a.Contains(s))
b.Remove(s);
}
You passing the bit in the if evaluation clause as a delegate (managed equivalent of a function pointer). The RemoveAll method unrolls the list and does the rest.
It says 'remove all elements in b that are contained in a'. So you're left only with the one's in b that weren't also present in a.
Here is a slightly expanded version of your code that shows what's happening:
List<string> a = new List<string> () { "a", "b", "c" };
List<string> b = new List<string> () { "a", "b", "c", "d", "e", "f" };
Predicate<string> ps = a.Contains;
b.RemoveAll (ps);
For every element in b, a.Contains() is being evaluated for that element. If it's true, it is removed.
So, you're removing every element from b that is also contained in a.
The function "a.Contains" is being passed as an argument to RemoveAll.
The signature for RemoveAll looks like this ...
public int RemoveAll(Predicate<T> match);
A Predicate<T> is a delegate that takes Ts and returns bools ...
public delegate bool Predicate<T>(T obj)
RemoveAll is thus asking for a reference to a method that will, in your case, take strings and return bools. List<T>.Contains is such a method. You will note that the signature for List<T>.Contains matches the Predicate delegate (it takes Ts and returns bools) ...
public bool Contains(T item);
RemoveAll will apply whatever Predicate is passed as "match" to each element of the list upon which it is called (b in your case). So, if a.Contains("a"), for example, returns true, then all the a's will be removed from the b list. Thus, in your example, all the a's, b's and c's are removed.
Using a tool like .NET Reflector will let you look at the code of RemoveAll and that might help clarify what's happening under the covers.
I've always wanted to be able to use the line below but the C# compiler won't let me. To me it seems obvious and unambiguos as to what I want.
myString.Trim({'[', ']'});
I can acheive my goal using:
myString.Trim(new char[]{'[', ']'});
So I don't die wondering is there any other way to do it that is closer to the first approach?
The string.Trim(...) method actually takes a params argument, so, why do you not just call:
myString.Trim('[', ']');
Others have concentrated on the specific example (and using the fact that it's a parameter array is the way to go), but you may be interested in C# 3's implicit typing. You could have written:
myString.Trim(new[] {'[', ']'});
Not quite as compact as you were after, as you still need to express the concept of "I want to create an array" unless you're writing a variable initializer, but the type of the array is inferred from the contents.
The big use case for this is anonymous types:
var skeets = new[] {
new { Name="Jon", Age=32 },
new { Name="Holly", Age=33 },
new { Name="Tom", Age=5 },
new { Name="Robin", Age=3 },
new { Name="William", Age=3 }
};
Here you couldn't write the name of the type, because it doesn't have a name (that's expressible in C#).
One other point to make about your specific example - if you're going to use this frequently (i.e. call the Trim method often) you may want to avoid creating a new array each time anyway:
private static readonly char[] SquareBrackets = {'[', ']'};
public void Whatever() {
...
foo = myString.Trim(SquareBrackets);
...
}
This will work too ...
myString.Trim( '[',']' );
Note the params declaration in the defition of Trim, it let's you pass as many arguments as you want and takes them as an array.
You could also try this:
myString.Trim("[]".ToCharArray());