Pass Method As EventDelegate - c#

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.

Related

C# Question about passing delegates as parameters

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.

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);
}
}

Passing null value to overloading method where Object and String as param in C#

I have two overloaded methods like below
public class TestClass
{
public void LoadTest(object param)
{
Console.WriteLine("Loading object...");
}
public void LoadTest(string param)
{
Console.WriteLine("Loading string...");
}
}
After calling this method like below it will show the output as Loading string... Please explain how .net handle this scenario?
class Program
{
static void Main(string[] args)
{
var obj=new TestClass();
obj.LoadTest(null);
// obj.LoadType(null);
Console.ReadLine();
}
}
The C# compiler takes the most specific overload possible.
As string is an object, and it can have the value of null, the compiler deems string to be more specific.
null is a valid string.
It will try to match the most specific type and string is more specific than object
Depending on your use you should probably remove the parameter totally in the object overload.
This is just the way C# compiler prioritizes to determine which method is better to call. There is one rule:
If method A has more specific parameter types than method B, then method A is better than method B in overload case.
In your case, apparently string is more specified than object, that is why LoadTest(string param) is called.
You can refer 7.5.3.2 Better function member in C# language specification to get more understanding.
because compiler takes the closest possible specific method that can be accessed (null to string is closer than null to object) that is available. And in this case as both as overload with string is closer, that is why it is called.
This is what MSDN has to say
Once the candidate function members and the argument list have been identified, the selection of the best function member is the same in
all cases:
Given the set of applicable candidate function members, the best function member in that set is located.
If the set contains only one function member, then that function member is the best function member.
Otherwise, the best function member is the one function member that is better than all other function members with respect to the given
argument list, provided that each function member is compared to all
other function members using the rules in Section 7.4.2.2.
If there is not exactly one function member that is better than all other function members, then the function member invocation is
ambiguous and a compile-time error occurs.

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

Cannot convert from 'method group' to 'System.Action<object>' error

I have created the following function:
public void DelegatedCall(Action<Object> delegatedMethod)
And defined the following method
public void foo1(String str) { }
However, when I try to call DelegateCall with foo1:
DelegatedCall(foo1);
...I get the following compiler error:
Argument 1: cannot convert from 'method group' to 'System.Action<object>'
What is the reason for this error and how can I correct it? Unfortunately, casting foo1 to Action is not an option.
DelegatedCall expects a delegate that takes any object as an argument. But your function foo1 that you are passing to DelegatedCall can only cope with a string argument. So, the conversion isn't type-safe and thus is not possible.
Input parameters are contra-variant, but your code needs covariance. (See Difference between Covariance & Contra-variance.)
You can make DelegatedCall generic:
DelegatedCall<T>(Action<T> action)
...or have it take any delegate:
DelegatedCall(Delegate action)
But then implementing it is ugly and requires reflection. It also doesn't verify that the function has only one parameter at compile-time.
Variance doesn't work that way around; you would need
DelegatedCall(obj => foo1((string)obj));
As even in 4.0 it won't believe that every object is a string.
Note that if it was foo1(object) and Action<string> (i.e. the other way around) it probably would work (in 4.0), since every string is an object.

Categories

Resources