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.
Related
I'm trying to create a Standard library with common types for WPF/UWP etc.
Since many types are not in Standard, sometimes you have to use reflection.
In this connection, the task of creating a DispatchedHandler from an Action arose.
In UWP, this is easy: new DispatchedHandler(action);.
But in Standard I only have Type from DispatchedHandler.
private object GetDispatchedHandler(Action action)
{
// Some code
dispatchedHandlerType = windowsAssembly.GetType("Windows.UI.Core.DispatchedHandler");
// Next, you need to transform the `action` and return it from the method.
return Activator.CreateInstance(dispatchedHandlerType, action);
}
This code throws an exception: "Constructor on type 'Windows.UI.Core.DispatchedHandler' not found".
But can get a constructor from a type: dispatchedHandlerType.GetConstructors()[0].
The constructor has the following signature: {Void .ctor(System.Object, IntPtr)}.
The first parameter, I think, should be passed action.
What should be passed as the second parameter?
dispatchedHandlerConsructor.Invoke(new object[] { action, ??? });
DispatchedHandler is a delegate type. It doesn't take an Action; it basically is the same thing as Action, but in an incompatible delegate type. The parameters here are the target instance and the function-pointer. You can create arbitrary delegate instances via Delegate.CreateDelegate. Usually you would create this type directly from a target method and instance, and never create the Action, however in this case you may also be able to simply pass in the .Target and .Method from the Action into your call to Delegate.CreateDelegate, since the signature is the same:
var del = Delegate.CreateDelegate(dispatchedHandlerType, action.Target, action.Method);
I have the following method in an assembly:
public string dostuff(string foo, object bar = null) { /* ... */ }
I use it as a callback, so a reference to it is passed to another assembly as such:
Func<string, object, string> dostuff
Now in the original form, I can call it without specifying that second argument, which defaults to null. But when I use it as a callback in that second assembly, I must specify that second argument.
What syntax allows me to ignore that second argument?
You'll need to create a new method that accepts only one argument, and that passes the default value for the second argument. You could do this with a lambda, rather than creating a new named method, if you wanted:
Func<string, string> doStuffDelegate = s => dostuff(s);
The other option would be to use a delegate who's signature has an optional second argument, instead of using Func, in which case your method's signature would match:
public delegate string Foo(string foo, object bar = null);
You could assign dostuff to a delegate of type Foo directly, and you would be able to specify only a single parameter when invoking that delegate.
You can't do this, simply because optional arguments are syntactic sugars and can be only used if you are calling the method directly. When you call the method like this:
dostuff(foo);
Compiler translates it into:
dostuff(foo, null);
In other cases such as using a delegate that doesn't accept an optional argument or when calling this method using reflection, you have to provide the optional argument.
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.
Any way to avoid explicitly declaring MyMethodDelegate in a scenario like this?
bool MyMethod(string x)
{
//...
}
BeginInvoke(new MyMethodDelegate(MyMethod), x);
I know about lambdas a-la ()=>MyMethod(x), however I want to avoid them sometimes as they break edit-and-continue.
Edit: just BeginInvoke(MyMethod, x) does not work:
The best overloaded method match for 'System.Windows.Forms.Control.BeginInvoke(System.Delegate, params object[])' has some invalid arguments
Argument 1: cannot convert from 'method group' to 'System.Delegate'
Argument 2: cannot convert from 'string' to 'object[]'
BeginInvoke is defined as follows:
public IAsyncResult BeginInvoke(Delegate method, params object[] args);
It does not specify any specific delegate type, so the compiler cannot detect which delegate type to instantiate from BeginInvoke(MyMethod. x)
For framework >= 3.5 you can use predefined delegates Action<> and Func<> (in your case)
BeginInvoke(new Func<int, bool>(MyMethod), x);
Docs for Func
http://msdn.microsoft.com/ru-ru/library/bb549151.aspx
You can often use the simplified version
MyMethod
when a delegate is required. the keyword is often.
However if the compiler can't determine which type of delegate to convert the method group to you will have to help out with an explicit conversion
Lambdas can come in handy when you wish to pass a function that you haven't already defined and don't need at other locations in the code. Then Lambdas (and anonymous functions in general) will be very handy since you can then simply define the function at the spot where you need it.
in the case of BeginInvoke you are correct as noted in the comments that you can't you will need to explicitly convert the method group to a delegate either by casting or by assignment
Func<int,bool) m = MyMethod;
BeginInvoke(m,x);
BeginInvoke((Func<inte,bool>)MyMethod,x);
will compile or you can pass in a lambda because that's interpreted as a Func
BeginInvoke(a=>MyMethod(a),x);
I am new to all the anonymous features and need some help. I have gotten the following to work:
public void FakeSaveWithMessage(Transaction t)
{
t.Message = "I drink goats blood";
}
public delegate void FakeSave(Transaction t);
public void SampleTestFunction()
{
Expect.Call(delegate { _dao.Save(t); }).Do(new FakeSave(FakeSaveWithMessage));
}
But this is totally ugly and I would like to have the inside of the Do to be an anonymous method or even a lambda if it is possible. I tried:
Expect.Call(delegate { _dao.Save(t); }).Do(delegate(Transaction t2) { t2.Message = "I drink goats blood"; });
and
Expect.Call(delegate { _dao.Save(t); }).Do(delegate { t.Message = "I drink goats blood"; });
but these give me
Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type** compile errors.
What am I doing wrong?
Because of what Mark Ingram posted, seems like the best answer, though nobody's explicitly said it, is to do this:
public delegate void FakeSave(Transaction t);
Expect.Call(delegate { _dao.Save(t); }).Do( new FakeSave(delegate(Transaction t2) { t.Message = expected_msg; }));
That's a well known error message. Check the link below for a more detailed discussion.
http://staceyw1.wordpress.com/2007/12/22/they-are-anonymous-methods-not-anonymous-delegates/
Basically you just need to put a cast in front of your anonymous delegate (your lambda expression).
In case the link ever goes down, here is a copy of the post:
They are Anonymous Methods, not
Anonymous Delegates.
Posted on December 22, 2007 by staceyw1
It is not just a talking point because
we want to be difficult. It helps us
reason about what exactly is going on.
To be clear, there is *no such thing
as an anonymous delegate. They don’t
exist (not yet). They are "Anonymous
Methods" – period. It matters in how
we think of them and how we talk about
them. Lets take a look at the
anonymous method statement "delegate()
{…}". This is actually two different
operations and when we think of it
this way, we will never be confused
again. The first thing the compiler
does is create the anonymous method
under the covers using the inferred
delegate signature as the method
signature. It is not correct to say
the method is "unnamed" because it
does have a name and the compiler
assigns it. It is just hidden from
normal view. The next thing it does
is create a delegate object of the
required type to wrap the method. This
is called delegate inference and can
be the source of this confusion. For
this to work, the compiler must be
able to figure out (i.e. infer) what
delegate type it will create. It has
to be a known concrete type. Let
write some code to see why.
private void MyMethod()
{
}
Does not compile:
1) Delegate d = delegate() { }; // Cannot convert anonymous method to type ‘System.Delegate’ because it is not a delegate type
2) Delegate d2 = MyMethod; // Cannot convert method group ‘MyMethod’ to non-delegate type ‘System.Delegate’
3) Delegate d3 = (WaitCallback)MyMethod; // No overload for ‘MyMethod’ matches delegate ‘System.Threading.WaitCallback’
Line 1 does not compile because the
compiler can not infer any delegate
type. It can plainly see the signature
we desire, but there is no concrete
delegate type the compiler can see.
It could create an anonymous type of
type delegate for us, but it does not
work like that. Line 2 does not
compile for a similar reason. Even
though the compiler knows the method
signature, we are not giving it a
delegate type and it is not just going
to pick one that would happen to work
(not what side effects that could
have). Line 3 does not work because
we purposely mismatched the method
signature with a delegate having a
different signature (as WaitCallback
takes and object).
Compiles:
4) Delegate d4 = (MethodInvoker)MyMethod; // Works because we cast to a delegate type of the same signature.
5) Delegate d5 = (Action)delegate { }; // Works for same reason as d4.
6) Action d6 = MyMethod; // Delegate inference at work here. New Action delegate is created and assigned.
In contrast, these work. Line 1 works
because we tell the compiler what
delegate type to use and they match,
so it works. Line 5 works for the
same reason. Note we used the special
form of "delegate" without the parens.
The compiler infers the method
signature from the cast and creates
the anonymous method with the same
signature as the inferred delegate
type. Line 6 works because the
MyMethod() and Action use same
signature.
I hope this helps.
Also see:
http://msdn.microsoft.com/msdnmag/issues/04/05/C20/
What Mark said.
The problem is that Do takes a Delegate parameter. The compiler can't convert the anonymous methods to Delegate, only a "delegate type" i.e. a concrete type derived from Delegate.
If that Do function had took Action<>, Action<,> ... etc. overloads, you wouldn't need the cast.
The problem is not with your delegate definition, it's that the parameter of the Do() method is of type System.Delegate, and the compiler generated delegate type (FakeSave) does not implicitly convert to System.Delegate.
Try adding a cast in front of your anonymous delegate:
Expect.Call(delegate { _dao.Save(t); }).Do((Delegate)delegate { t.Message = "I drink goats blood"; });
Try something like:
Expect.Call(delegate { _dao.Save(t); }).Do(new EventHandler(delegate(Transaction t2) { t2.CheckInInfo.CheckInMessage = "I drink goats blood"; }));
Note the added EventHandler around the delegate.
EDIT: might not work since the function signatures of EventHandler and the delegate are not the same... The solution you added to the bottom of your question may be the only way.
Alternately, you could create a generic delegate type:
public delegate void UnitTestingDelegate<T>(T thing);
So that the delegate is not Transaction specific.