I'm curious why neither of the following DoInvoke methods can be called with only one params:
public class foo {
private void bar(params object[] args) {
DoInvoke(args);
}
//Error: There is no argument given that corresponds to the required formal parameter 'args' of 'foo.DoInvoke(Delegate, object[])'
private void DoInvoke(Delegate d, object[] args) {
d.DynamicInvoke(args);
}
//Error: Argument 1: cannot convert from 'object[]' to 'System.Delegate'
private void DoInvoke(Delegate d, params object[] args) {
d.DynamicInvoke(args);
}
}
I already found a way that doesn't abuse params. I'm curious why params are not expanded here.
I was able to do something similar in Lua, hence my attempt. I know Lua is far less strict, but I'm not sure which C# rule I'm breaking by doing this.
I'm curious why neither of the following DoInvoke methods can be called with only one params:
Short version: the first can't, because it has two non-optional parameters and because you're passing a value of the wrong type for the first non-optional parameter. The second can't, but only because the value you are trying to pass for the single non-optional parameter is of the wrong type; the second parameter is optional and so may be omitted as you've done.
You seem to be under the impression that in your method declaration private void bar(params object[] args), the presence of the params keyword makes the args variable somehow different from any other variable. It's not. The params keyword affects only the call site, allowing (but not requiring) the caller to specify the array elements of the args variable to be specified as if they were individual parameters, rather than creating the array explicitly.
But even when you call bar() that way, what happens is that an array object is created and passed to bar() as any other array would be passed. The variable args inside the bar() method is just an array. It doesn't get any special handling, and the compiler won't (for example) implicitly expand it to a parameter list for use in passing to some other method.
I'm not familiar with Lua, but this is somewhat in contrast to variadic functions in C/C++ where the language provides a way to propagate the variable parameter list to callees further down. In C#, the only way you can directly propagate a params parameter list is if the callee can accept the exact type of array as declared in the caller (which, due to array type variance in C#, does not always have to be the exact same type, but is still limited).
If you're curious, the relevant C# language specification addresses this in a variety of places, but primarily in "7.5.1.1 Corresponding parameters". This reads (from the C# 5 specification…there is a draft C# 6 specification, but the C# 5 is basically the same and it's what I have a copy of):
For each argument in an argument list there has to be a corresponding parameter in the function member or delegate being invoked.
It goes on to describe what "parameter list" is used to validate the argument list, but in your simple example, overload resolution has already occurred at the point this rule is being applied, and so there's only one parameter list to worry about:
• For all other function members and delegates there is only a single parameter list, which is the one used.
It goes on to say:
The corresponding parameters for function member arguments are established as follows:
• Arguments in the argument-list of instance constructors, methods, indexers and delegates:
o A positional argument where a fixed parameter occurs at the same position in the parameter list corresponds to that parameter. [emphasis mine]
o A positional argument of a function member with a parameter array invoked in its normal form corresponds to the parameter array, which must occur at the same position in the parameter list.
o A positional argument of a function member with a parameter array invoked in its expanded form, where no fixed parameter occurs at the same position in the parameter list, corresponds to an element in the parameter array.
o A named argument corresponds to the parameter of the same name in the parameter list.
o For indexers, when invoking the set accessor, the expression specified as the right operand of the assignment operator corresponds to the implicit value parameter of the set accessor declaration.
In other words, if you don't provide a parameter name in your argument list, arguments correspond to method parameters by position. And the parameter in the first position of both your called methods has the type Delegate.
When you try to call the first method, that method has zero optional parameters, but you haven't provided a second parameter. So you get an error telling you that your argument list, consisting of just a single argument (which by the above corresponds to the Delegate d parameter), does not include a second argument that would correspond to the object[] args parameter in the called method.
Even if you had provided a second argument, you would have run into the same error you get trying to call your second method example. I.e. while the params object[] args parameter is optional (the compiler will provide an empty array for the call), and so you can get away with providing just one argument in your call to the method, that one argument has the wrong type. Its positional correspondence is to the Delegate d parameter, but you are trying to pass a value of type object[]. There's no conversion from object[] to Delegate, so the call fails.
So, what's that all mean for real code? Well, that depends on what you are trying to do. What did you expect to happen when you tried to pass your args variable to a void DoInvoke(Delegate d, params object[] args) method?
One obvious possibility is that the args array contains as its first element a Delegate object, and the remainder of the array are the arguments to pass. In that case, you could do something like this:
private void bar(params object[] args) {
DoInvoke((Delegate)args[0], args.Skip(1).ToArray());
}
That should be syntactically valid with either of the DoInvoke() methods you've shown. Of course, whether that's really what you want is unclear, since I don't know what the call was expected to do.
Related
I have a class with an overloaded Format method.
class FormatStuff
{
public static string Format(object arg)
=> HandleObjectStuff();
public static string Format(IEnumerable<object> args)
=> HandleListStuff();
}
Now, when I call
FormatStuff.Format(null);
I end up in the second overload with the IEnumerable parameter.
But in my case, I call the method from within a function like this:
public static string DoStuff(IEnumerable<int> intnumerable)
{
StringBuilder sb = new StringBuilder();
sb.Append(FormatStuff.Format(intnumerable));
return sb.ToString();
}
When I call this function like
DoStuff(null);
I end up in the first overload with the single object parameter, even though in both cases null is passed as the parameter.
Why is this and what can I do to end up in the second overload that matches the type of the DoStuff-parameter?
Edit:
The question has been marked as a possible duplicate of this one. I don't think that's entirely the case, because the salient point that helped me understand my problem was, that an IEnumerable<int> is not an IEnumerable<object>.
In general that means, that one cannot expect an IEnumerable of any type to be an IEnumerable of object, which I did not know.
This conclusion is not drawn in the mentioned post.
Which overload to call (binding) is statically fixed for each invocation expression at compile-time (unless you use type dynamic at compile-time). Just because the expression you use for argument happens to evaluate to another type when the program runs, the overload will not magically change.
Examples:
FormatStuff.Format(null);
The compile-time type does not exist (null), but since there is an implicit conversion from the null literal to object and an implicit conversion from null to IEnumerable<object> as well, both overloads are candidates. In that case the overload with IEnumerable<object> is preferred because it is more specific.
FormatStuff.Format((object)null);
In this case the compile-time type of the expression is object, so only one overload applies, and that is used.
IEnumerable<int> intnumerable
// ...
FormatStuff.Format(intnumerable);
In the above case the compile-time type of what you pass is IEnumerable<int>. Here int is a value-type. An IEnumerable<int> is not an IEnumerable<object> at compile-time. This is fixed at compile-time; it does not matter whether intnumerable happens to be null at run-time, and if non-null, it does not matter what the actual type (some concrete class or struct implementing IEnumerable<int>) is at run-time.
IEnumerable<string> strEnumerable
// ...
FormatStuff.Format(strEnumerable);
Finally, in this case, since string is a reference type, the compile-time covariance of IEnumerable<out T> applies. So an IEnumerable<string> is an IEnumerable<object>. Therefore both overloads apply, and the most specific one is preferred.
To achieve what you want, your method must be able to distinguish between the two method calls.
When you pass null the Format() method doesn't know if your null is an object or IEnumerable<object> since both are of type object.
To solve your issue you can do one of the following:
1 Change the second method as Format(IEnumerable<int> args)
OR
2 Change the type signature of your method by adding optional arguments. Take this as an example
Given this code:
class C
{
C()
{
Test<string>(A); // fine
Test((string a) => {}); // fine
Test((Action<string>)A); // fine
Test(A); // type arguments cannot be inferred from usage!
}
static void Test<T>(Action<T> a) { }
void A(string _) { }
}
The compiler complains that Test(A) can't figure out T to be string.
This seems like a pretty easy case to me, and I swear I've relied far more complicated inference in other generic utility and extension functions I've written. What am I missing here?
Update 1: This is in the C# 4.0 compiler. I discovered the issue in VS2010 and the above sample is from a simplest-case repro I made in LINQPad 4.
Update 2: Added some more examples to the list of what works.
Test(A);
This fails because the only applicable method (Test<T>(Action<T>)) requires type inference, and the type inference algorithm requires that each each argument be of some type or be an anonymous function. (This fact is inferred from the specification of the type inference algorithm (§7.5.2)) The method group A is not of any type (even though it is convertable to an appropriate delegate type), and it is not an anonymous function.
Test<string>(A);
This succeeds, the difference being that type inference is not necessary to bind Test, and method group A is convertable to the required delegate parameter type void Action<string>(string).
Test((string a) => {});
This succeeds, the difference being that the type inference algorithm makes provision for anonymous functions in the first phase (§7.5.2.1). The parameter and return types of the anonymous function are known, so an explicit parameter type inference can be made, and a correspondense is thereby made between the types in the anonymous function (void ?(string)) and the type parameter in the delegate type of the Test method’s parameter (void Action<T>(T)). No algorithm is specified for method groups that would correspond to this algorithm for anonymous functions.
Test((Action<string>)A);
This succeeds, the difference being that the untyped method group parameter A is cast to a type, thereby allowing the type inference of Test to proceed normally with an expression of a particular type as the only argument to the method.
I can think of no reason in theory why overload resolution could not be attempted on the method group A. Then—if a single best binding is found—the method group could be given the same treatment as an anonymous function. This is especially true in cases like this where the method group contains exactly one candidate and it has no type parameters. But the reason it does not work in C#4 appears to be the fact that this feature was not designed and implemented. Given the complexity of this feature, the narowness of its application, and the existance of three easy work-arounds, I am not going to be holding my breath for it!
I think it's because it's a two-step inference:
It has to infer that you want to convert A to a generic delegate
It has to infer what the type of the delegate parameter should be
I'm not sure if this is the reason, but my hunch is that a two-step inference isn't necessarily easy for the compiler.
Edit:
Just a hunch, but something is telling me the first step is the problem. The compiler has to figure out to convert to a delegate with a different number of generic parameters, and so it can't infer the types of the parameters.
This looks like a vicious circle to me.
Test method expects a parameter of delegate type constructed from generic type Action<T>. You pass in a method group instead: Test(A). This means compiler has to convert your parameter to a delegate type (method group conversion).
But which delegate type? To know the delegate type we need to know T. We didn't specify it explicitly, so compiler has to infer it to figure out the delegate type.
To infer the type parameters of the method we need to know the types of the method arguments, in this case the delegate type. Compiler doesn't know the argument type and thus fails.
In all other cases either type of argument is apparent:
// delegate is created out of anonymous method,
// no method group conversion needed - compiler knows it's Action<string>
Test((string a) => {});
// type of argument is set explicitly
Test((Action<string>)A);
or type parameter is specified explicitly:
Test<string>(A); // compiler knows what type of delegate to convert A to
P.S. more on type inference
You're passing the name of the Method A. The .Net framework CAN convert it to an Action, but it's implicit and it will not take responsibility for it.
But still, a method name is NOT an explicit Action<> Object. And therefor it won't infer the type as an Action type.
I could be wrong, but I imagine the real reason C# cannot infer the type is due to method overloading and the ambiguity that arises. For example, suppose I have the following methods: void foo (int) and void foo (float). Now if I write var f = foo. Which foo should the compiler pick? Likewise, the same problem happens with your example using Test(foo).
Say I have the following methods:
public static void MyCoolMethod(params object[] allObjects)
{
}
public static void MyCoolMethod(object oneAlone, params object[] restOfTheObjects)
{
}
If I do this:
MyCoolMethod("Hi", "test");
which one gets called and why?
It's easy to test - the second method gets called.
As to why - the C# language specification has some pretty detailed rules about how ambiguous function declarations get resolved. There are lots of questions on SO surrounding interfaces, inheritance and overloads with some specific examples of why different overloads get called, but to answer this specific instance:
C# Specification - Overload Resolution
7.5.3.2 Better function member
For the purposes of determining the
better function member, a
stripped-down argument list A is
constructed containing just the
argument expressions themselves in the
order they appear in the original
argument list.
Parameter lists for each of the
candidate function members are
constructed in the following way:
The expanded form is used if
the function member was applicable
only in the expanded form.
Optional parameters with no
corresponding arguments are removed
from the parameter list
The parameters are reordered
so that they occur at the same
position as the corresponding argument
in the argument list.
And further on...
In case the parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …, QN} are equivalent > (i.e. each Pi has an identity conversion to the corresponding Qi), the following
tie-breaking rules are applied, in order, to determine the better function member.
If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
Otherwise, if MP is applicable in its normal form and MQ has a params array and is
applicable only in its expanded form, then MP is better than MQ.
Otherwise, if MP has more declared parameters than MQ, then MP is better than MQ.
This can occur if both methods have params arrays and are applicable only in their
expanded forms.
The bolded tie-breaking rule seems to be what is applying in this case. The specification goes into detail about how the params arrays are treated in normal and expanded forms, but ultimately the rule of thumb is that the most specific overload will be called in terms of number and type of parameters.
The second one, the compiler will first try to resolve against explicitly declared parameters before falling back on the params collection.
This overload is tricky...
MyCoolMethod("Hi", "test") obviously calls the 2nd overload, but
MyCoolMethod("Hi"); also calls the 2nd overload. I tested this.
Maybe since both of the inputs are objects, the compiler assume anything passed in will be an array of objects and completely ignores the 1st overload.
It probably has to do with the Better function member resolution mentioned by womp
http://msdn.microsoft.com/en-us/library/aa691338(v=VS.71).aspx
I have the following delegate:
public delegate object DynamicFunction(dynamic target, params object[] args);
However, when I try to create it:
DynamicFunction func = new DynamicFunction((t) => {
//Handle t
});
The compiler throws an error saying that the delegate does not take 1 argument, even though I specified the last argument to be of type params object[].
If I pass exacly one extra argument to the delegate, it works.
For example:
DynamicFunction func = new DynamicFunction((t,a) => {
//Handle t
});
However, I don't want to specify that extra argument, as I intentionally wanted those arguments to be optional.
What is happening here?
params mean that compiler will do smart thing when you call the function to convert whatever arguments you've passed to array.
It does not mean that function itself takes 1 or 2 parameters, and it does not mean there are 2 version of function f(dynamic target) and f(dynamic target, params object[] args).
Note that you still want to be able to call
func (1);
func(1, optional1, optional2);
So your delegate need to handle both.
When invoking the delegate the caller can provide 1...n arguments. What you're doing now isn't invoking the delegate, but rather assigning a compatible method to a delegate of that type. When doing so you need to provide exactly two arguments (of the appropriate type), because that's what the delegate's definition states it must accept.
I am working on an asp.net mvc web application, and i need to pass two params expression as follow:-
public RackJoin AllFindDetails(int id, params Expression<Func<Server, object>>[] includeProperties,params Expression<Func<Resource, object>>[] includeProperties2)
{
but the above will raise the following error:-
A parameter array must be the last parameter in a formal parameter list
You need to find a different solution. Since params just takes every argument given it and stuffs them into an array, two instances of it on the same function don't make sense.
MSDN formalizes this:
No additional parameters are permitted after the params keyword in a
method declaration, and only one params keyword is permitted in a
method declaration.
The specification is slightly less specific (Section 1.6.6.1):
Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type.
But only allowing the last parameter to be params implies there can only be one, since a second one by definition could not be the last one as well.