When declaring variables of the same type, we normally do this:
int a,b,c,d;
Is there a construct to do something similar with function parameters?
This function would take 3 integers:
void foo(int a,b,c)
{
}
No, there is no such construct for declaring method arguments. You must declare your parameters one by one.
The closest thing that lets your method receive multiple parameters declared as a single array parameter is params:
void Foo(params int[] a) {
...
}
This method can be called as follows:
Foo(a, b, c, d);
The caller can pass any number of separate parameters, including zero. Your method would receive all of them in a single array.
No, there's not. This is the documentation about arguments:
https://msdn.microsoft.com/en-us/library/aa691335(v=vs.71).aspx
Related
Given a func that takes N parameters, why is it possible to assign a delegate to it that doesn't explicitly declare any parameters? e.g.
Func<int, string, object, string, bool> test;
// (1) this makes sense to me
test= delegate (int a, string b, object c, string d) { return true; };
// (2) this also makes sense to me
test= (a,b,c,d)=>true;
// (3) why does this work?
test = delegate { return true; };
Why does (3) work? Is there any difference between (1), (2), and (3)? Can we access the parameters from inside the braces of the third variation?
Why does (3) work?
From the C# programming guide on MSDN:
Anonymous methods enable you to omit the parameter list. This means that an anonymous method can be converted to delegates with a variety of signatures
Is there any difference between (1), (2), and (3)?
delegate keyword vs. lambda notation
Can we access the parameters from inside the braces of the third variation?
No. Don't omit the parameter list if you intend to use the parameters in your anonymous method.
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.
I have a delegate which looks like the following:
public delegate bool ApprovalPrompt(ApprovalType type, int receipt, params string[] info);
I accept a delegate of this type as a parameter to the function I want to call. However, in one particular calling function, I want to pass some extra data to the function which matches this delegate.
Here's the signature of the implementing function:
private static bool LogApprovalNeeded(FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)
and it's being called as follows:
PrepareReceipt(LogApprovalNeeded);
I'd like it to be:
private static bool LogApprovalNeeded(Customer cust, FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)
which ideally would be used as follows:
PrepareReceipt(LogApprovalNeeded(myCustomer))
How can I accomplish such a thing? I'd rather not need to declare a field in the class just to hold the Customer parameter between one function and the callback...
You can use a lambda to "curry" your function:
PrepareReceipt((type, receipt, info) =>
LogApprovalNeeded(myCustomer, type, receipt, info))
Currying a function is the formal term for storing a reference to a function but with one or more of the parameters "fixed", thus altering the signature of the method.
You can also use a lambda when the signature of your function doesn't need all of the arguments that the delegate is providing; you can effectively discard parameters by not passing forward all of the arguments in the lambda.
You can use a lambda to achieve what you need.
PrepareReceipt((type, receipt, info) =>
LogApprovalNeeded(myCustomer, type, receipt, info));
Alternatively, change your LogApprovalNeeded signature to:
static bool LogApprovalNeeded(ApprovalType type, int receipt,
Customer cust = null, params string[] info)
{
}
But it could get a bit confusing, considering that you already have a variable number of parameters defined after cust.
EDIT: As Servy rightfully pointed out, the change of signature won't let you call the method as you described. If you move the logic related to Customer to PrepareReceipt, though, you won't need to use the above approach (which basically generates a new anonymous method and wraps myCustomer in a closure.
If you need generic solution for delegates partial application (parameters reduction) take a look to the NReco Commons open source library, it contains PartialDelegateAdapter that can do that for any delegate type:
var logApprovalForCustomer = (new PartialDelegateAdapter(LogApprovalNeeded,
new[] {myCustomer})).GetDelegate<Func<FraudFilterUtilities.ApprovalType,int,string[],bool>>();
in this example 1st parameter is fixed with myCustomer value. In addition it also tries to harmonize argument types in runtime.
You can change the PrepareReceipt function to take an additional parameter. The signature would look something like public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt) to accomplish this.
You can't pass it to that delegate as the delegate does not declare an argument of type Customer. The "simple answer" would be to change the signature of the delegate to take the new argument.
That said, that would also require modification of all the consumers of the delegate.
I want to implement a method that will find stuff in my custom class. It should work like generic collections work - i pass a pointer to a function, and the method will iterate through all it has to look in, apply this function, and if it returns true return the found item.
I'd like to pass function pointer as a parameter, but i dont want to declare delegate types.
I know i can do something like:
delegate bool Foo(MyClass)
MyClass MyMethod(Foo x)
{...}
And i know i can do something like this:
MyClass MyMethod(Func<MyClass,bool> x)
But can i do it without declaring a delegate type and without using built in stuff like Func<> which has limits on how many parameters i can have (in case of Func, one...)
You can just use delegate if you want, although it's a bit old school :)
public void TestInvokeDelegate()
{
InvokeDelegate( new TestDelegate(ShowMessage), "hello" );
}
public void InvokeDelegate(TestDelegate del, string message)
{
del(message);
}
public delegate void TestDelegate(string message);
public void ShowMessage(string message)
{
Debug.WriteLine(message);
}
You can allways pass in a Delegate and call DynamicInvoke on it:
MyClass MyMethod(Delegate x) {
// ...
x.DynamicInvoke(....);
// ...
}
It looks like you are trying to implement the Visitor pattern. In this case visiting methods usually have only one parameter - the instance to visit. Having additional arguments passed around conceals the use of the pattern and makes it harder to reason about. This article shows you one way to implement it in C#.
The key is to create a visitor class that will encapsulate all the parameters that affect the visiting process. This way you don't need to pass anythnig other than an object in question in the visiting method - everything else lives in instance fields.
However, if you really want to pass some additional parameters in the method and don't know what type they can have, there are ways to do that. More or less standard approach in .NET world is to use a delegate without return value and with single parameter of type object, the example would be ParameterizedThreadStart delegate:
public delegate void ParameterizedThreadStart(
Object obj
)
This way you get to pass only one parameter in the delegate, but it could be anything - an instance of a class, an array or null, if you end up not needing additional arguments after all. The downside of this approach is that it requires type casting which can lead to runtime errors.
I'm trying to do this:
public void CustomMethod(params int[] number,params string[] names)
{
...
}
If i delete one of them , there is no problems , any idea of why i can't do this?
I have tried putting a normal parametre in the middle of both.
Only the last parameter can have params. See the documentation.
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 reason is that allowing multiple params would give ambiguity. For example, what would this mean?
public void CustomMethod(params int[] foo, params int[] bar)
{
...
}
// ...
CustomMethod(1, 2);
This is simply not supported. The compiler can't know when one parameter list ends and the next begins.
As far as I know, you can only write one params parameter in the constructor which shall be the last parameter of the constructor.
The params keyword lets you specify a method parameter that takes an argument where the number of arguments is variable.
No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration.
See Here : http://msdn.microsoft.com/en-us/library/w5zay9db(v=VS.71).aspx