C# Question about passing delegates as parameters - c#

I have a question regarding delegates. So in the Threading class, there is a method called public Thread(ThreadStart start) this method takes in a parameter of ThreadStart which is a void delegate defined by Microsoft. What I can't seem to wrap my head around is the fact if I pass a method inside the parameter of Thread(ThreadStart start) it doesn't give me an error which is great. But why is it when I try to pass a delegate inside this parameter that is not type ThreadStart it gives me an error. I understand the signature of this method takes a delegate of type ThreadStart but then why can I put any method in without an issue but forced to use a delegate of ThreadStart for it not to give me an error. But I can enter any method I want as long as it doesn't have a parameter. Sorry if this is a basic question when am doing the stuff I like to know why a certain thing does something otherwise it bugs me. My guess is that maybe when you define a parameter with a type of delegate. It will let you enter any method you want as long as it fits the delegate's signature. But when you pass a delegate inside a parameter that is a specific delegate it will want you to use the same delegate as the type?

In general you can turn a parameterized method into a non parameterized signature call
Just do something like:
var myThread = new Thread(() => MethodWithParemeters(Parameter));
doing () => Foo() uses linq (I think) to create a new method that calls Foo

When a delegate is defined it attaches the signature to the name. So for:
public delegate void ThreadStart();
So any ThreadStart delegate must have a matching signature. So a function with another signature can't be passed to anything expecting a ThreadStart delegate as the parameter.
Since you raised the issue. I suspect what your code is needing is the constructor:
Thread(ParameterizedThreadStart)
Where ParameterizedThreadStart is:
public delegate void ParameterizedThreadStart(object obj);
And the object that you pass is your own class or structure.

If I understand the question correctly you are asking why Case 1 succeed, but case 3 does not compile:
public delegate void MyDelegate1();
public delegate void MyDelegate2();
public void MyMethod() { }
public void AcceptDelegate(MyDelegate1 md) { }
public void Example()
{
AcceptDelegate(MyMethod); // Case 1: Works
AcceptDelegate(() => {}); // Case 2: Also Works
MyDelegate2 delegate2 = MyMethod;
AcceptDelegate(delegate2); // Case 3: Fails, cannot convert delegate
AcceptDelegate(new MyDelegate1(delegate2)); // Case 4: Works, since an explicit conversion was used
AcceptDelegate(() => delegate2());// Case 5: Works
}
The reason that case 3 fails is that the delegate types are different.
The reason that case 1 work is that the compiler uses the overload resolution rules to find a method with name MyMethod and have a compatible signature to MyDelegate. I'm not knowledgeable enough about the compiler to cite the exact rules that applies in this specific case. The important thing is that MyMethod is not e delegate, it is a 'method group' that can resolve down to a specific method that can be converted to a delegate.
See How to convert delegate to identical delegate for more additional info.

Related

Can I define lambda default for some argument? [duplicate]

Is there a way to use optional arguments (default parameters) with lambda expressions in c#? I have read through the documentation but can find nothing to say one way or the other.
To illustrate, I can define simple method that uses an optional argument to supply a default value like so:
void MyMethod(string arg = "default-value")
{
Console.WriteLine(arg);
}
What I want to know is if I am able to do the same thing using a lambda expression.
// gives a syntax error
Action<string> MyMethod = (arg = "default") => Console.WriteLine(arg);
I can work in an optional parameter with a default value using a delegate, but this seems a bit clumsy.
delegate void MyDelegate(string arg = "default");
MyDelegate MyMethod = arg => Console.WriteLine(arg);
Alternatively I could check the parameter in the lambda body, something like...
Action<string> MyMethod = (arg) => Console.WriteLine(string.IsNullOrEmpty(arg) ?
"default" :
arg);
But again this seems a bit clumsy.
Is it possible to use optional parameters to set a default value in a lambda expression in c#?
No. The caller (the code invoking the delegate) doesn't "see" the lambda expression, so it doesn't make sense to specify the default parameter there. All the caller sees is the delegate. In your case, for example, the calling code only knows about Action<string> - how is the compiler meant to know to supply the default value that's specified by the lambda expression?
As an example of how things get tricky, imagine if this were viable. Then consider this code:
Action<string> action;
if (DateTime.Today.Day > 10)
{
action = (string arg = "boo") => Console.WriteLine(arg);
}
else
{
action = (string arg = "hiss") => Console.WriteLine(arg);
}
action(); // What would the compiler do here?
Bear in mind that the argument is provided by the compiler at the call site - so what should it do with the final line?
It's a bit like having an interface and an implementation - if you have a default parameter on an interface, that's fine; if you only have it on the implementation, then only callers who know the specific implementation will see it. In the case of lambda expressions, there's really no visible implementation for the caller to use: there's just the delegate signature.
The lambda will match whatever the signature of the delegate it's assigned to is; without being assigned to a delegate a lambda cannot compile.
If the delegate contains optional arguments then the use of that delegate can optionally supply arguments. If the delegate doesn't, then the use of that delegate cannot omit any arguments.
While the Action and Func delegates are very handy, and can represent most signatures, they can't represent any signature with optional arguments; you must use another delegate definition for that.
Remeber, Action and Func aren't particularly special, they're just two delegates that everyone uses so that they don't need to worry about creating their own for every little thing.

Pass Method As EventDelegate

I am attempting to pass a method as a parameter. But I am getting a compiler error and I dont quite understand what I am doing wrong?
The compiler error is:
Argument `#1' cannot convert 'method group' expression to type 'EventDelegate'
public class AssignEventDelegate : MonoBehaviour {
public UISprite sprite;
public void AddOnFinish (EventDelegate method) {
EventDelegate.Add (sprite.GetComponent<TweenScale>().onFinished, method);
}
}
public class AssignEventDelegateOther : MonoBehaviour {
// Use this for initialization
void Start () {
// Compiler error occurs below
GameObject.Find ("Main Camera").gameObject.GetComponent<AssignEventDelegate>().AddOnFinish( myOnFinish );
}
public void myOnFinish () {
Debugger.print ("myOnFinish");
}
}
First let's make sense of the error message. It should be clear that the problematic part of that line is
.AddOnFinish( myOnFinish );
So what's a method group? That has been answered here before. In short, it is what the compiler calls a bare reference to a method name that does not include () or any argument list. You can't use a method group on its own. It does not have any representation in the CLR. Its a method "group" because, at least in theory, it represents a set of methods that the compiler will have to do overload resolution on. The compiler knows how to turn method groups (also lambdas and anonymous methods) into specific delegate types, but that's it. myOnFinish is a method group, but here it really only contains a single method.
A quick look at the NGUI docs tells me that contrary to its name, EventDelegate is not a delegate type but simply a class that is meant to wrap a delegate.
So there we have it. The compiler has no idea how to convert a method group to this EventDelegate class and it produces an error.
To fix it, we need to provide the conversion. Which it turns out, would just be calling the constructor of EventDelegate:
.AddOnFinish( new EventDelegate(myOnFinish) );
This works since the constructor takes an instance of EventDelegate.Callback which is delegate type that is compatible with myOnFinish.
Instead of passing method as argument define the delegate having same signature as method and pass it as argument to function.

Is there a way to cause type inference of the delegate passed to Control.BeginInvoke?

Control.BeginInvoke:
In both cases, it seems clear that the compiler has all the information it needs to infer the delegate type. Yet in neither case does the type inference seem to work:
BeginInvoke(myMethodThatTakesNoParams);
produces the compiler error
Error 105 The best overloaded method match for
'System.Windows.Forms.Control.BeginInvoke(System.Delegate)' has some
invalid arguments
as does
BeginInvoke(ShowProcessErrors, new object[] { process });
Both method calls only compile if I change them to explitly create a delegate and pass that. Both of the following compile fine:
BeginInvoke(new MethodInvoker(myMethodThatTakesNoParams));
and
BeginInvoke(new ProcessErrorDelegate(ShowProcessErrors), new object[] { process });
There doesn't seem to be any obvious reason why type inference won't work here. Is there a way to call BeginInvoke without explicitly creating a delegate?
The issue is that myMethodThatTakesNoParams isn't really a delegate but a so-called "method group" by the compiler. A method group isn't a real type in the CLR. It must be converted to delegate type to be used. When you use a method group like this:
Action a = myMethodThatTakesNoParams;
The compiler recognizes that you want to convert the method group to a delegate and inserts the conversion for you. It produces IL that effectively says:
Action a = new Action(myMethodThatTakesNoParams);
When you say:
Delegate d = myMethodThatTakesNoParams
The compiler doesn't really know what to do. It could theoretically pick any compatible delegate type for you, but C#, in general, does not insert types you did not use into expressions. Since it does not know what delegate you want the method group converted to, the compiler produces an error.
I used variable assignment in my examples, but the same logic applies for parameters to methods.
A work around would be to write your own extension method that has a specific delegate type in it:
static class ControlExtensions
{
public static IAsyncResult BeginInvoke(this Control c, Action a)
{
return c.BeginInvoke(a);
}
}
This usually comes as a surprise to .NET programmers, the C# Language Specific in section 15.1 explains:
Note that System.Delegate is not itself a delegate type; it is a class type from which all delegate types are derived
And of course there is no conversion of a method to a class. The first argument of BeginInvoke() must be a delegate type to keep the compiler happy. Maybe that sounds like an arbitrary limitation, it is most definitely not. A very important property of delegates is that they are type-safe. A pretty big deal in a statically typed language like C#. You can't invoke a delegate with too few arguments, or too many, or arguments of the wrong type. Checked when the delegate is created, you get a compile time error while you are still in your pajamas or the comfort of your cubicle. No surprises at runtime with your program suddenly keeling over at the most inopportune time of the day. This type checking of course cannot work for Delegate. So it is not a delegate type.
This does go wrong with Control.BeginInvoke(), it uses a back-door to get the method invoked. Kaboom when you pass Math.Pi instead of progress, you can't find out until you run the code. Not a pleasant exception either because it is unclear whether you got the BeginInvoke() call wrong or whether the invoked method threw an exception. Actually much more of a problem with Invoke().
Anyhoo, gotta give the compiler a delegate, more than one way to do that:
The venerable anonymous method syntax still works pretty well in this context:
this.BeginInvoke(delegate() { ShowProcessErrors(process); });
You already found MethodInvoker, I usually go for Action since it is shorter:
this.BeginInvoke(new Action(() => ShowProcessErrors(process)));
And you can of course always keep the compiler happy with an extension method:
this.BeginInvoke(() => ShowProcessErrors(process));
with:
static class Extensions {
public static void BeginInvoke(this Control ctl, Action a) {
ctl.BeginInvoke(a);
}
}

Are method names implicitly cast to delegate types?

Im having a little trouble understanding delegates.
I have a delegate that i will invoke when a y character is entered:
public delegate void respondToY(string msgToSend);
private respondToY yHandler;
i have a subscribe method in order that calling code can ask to be notified when the delegate is invoked:
public void Subscribe(respondToY methodName)
{
yHandler += methodName;
}
As far as i can see, to register with this delegate, i need to provide something of the type respondToY. Yet when calling the subscribe method, i can supply either a new instance of the delegate or simply the name of the method. Does this mean that any method matching the delegate signature can be used and will automatically be converted to the correct delegate type?
** Edit **
So on this assumption it would also be valid to supply only a method name to things like click event handlers for buttons (provided the method took the sender, and the relevant event object), it would be converted to the required delegate?
This is a method group conversion. It converts a method group (basically the name of a method or overloaded methods) to an instance of a delegate type with a compatible signature.
Yes, any compatible method can be used. Note that you can provide a target too - for example:
string text = "Hello there";
Func<int, int, string> func = text.Substring;
Console.WriteLine(func(2, 3)); // Prints "llo", which is text.Substring(2, 3)
There must be a specific delegate type involve though. You can't just use:
Delegate x = methodName;
... the compiler doesn't know the kind of delegate to create.
For more information, see section 6.6 of the C# 4 language specification.
Note that a method group conversion always creates a new instance of the delegate in question - it isn't cached (and can't be without violating the specification.)
as far as i know ... yes, the delegate type just makes sure that the signatures match

Contravariant parameters?

This is probably a stupid question but, I have a method I use to make the syntax of a page a little more easy to read
public void Do(Delegate method, DispatcherPriority priority = DispatcherPriority.Normal)
{
this.Window.Dispatcher.BeginInvoke(method, DispatcherPriority.Background);
}
then I can write
Do(new Action(() =>
{
//DoStuff()
}));
However, I'd like to move the Action up into the Do Method so I can write more Simply:
Do(() =>
{
//DoStuff()
}));
But I'm a bit sure how to write the contravariant parameter to do the Do Method?
Lambdas are untyped, so this is not possible.
If you don't care about method-arguments, which appears to be the case, why not change the method signature to :
public void Do(Action method,
DispatcherPriority priority = DispatcherPriority.Normal)
Then, the second sample will work fine since the compiler will be able to implicitly convert the lambda to an Action.
If you really want to accept an instance of any delegate-type that represents a method that takes no arguments, you'll have to stick to something like what you currently have.

Categories

Resources