I have just seen following code:
class X
{
static Action Ac()
{
return ..some other code
}
}
What does it mean? I have never seen a delegate with its body declared.
That's not an Action delegate with its body declared. That's a static method of the X class called Ac(), with a return type of Action; that is, it's a class method that returns an Action delegate. The body presumably creates an Action object to return from the method.
To put it another way: it is a regular static method, which happens to return Action instead of something like string or int.
Delegate object which references anonymous method is declared something like (using old syntax and omitting lambda notation):
Action<int> action = delegate (int x) {
//this is a body of anonymous method
//which is referenced by a delegate object action of type Action<int>
Console.WriteLine (x);
};
And than called like:
action(5);
Related
I am trying to learn Action delegate and lambda in C#.
So far I came to know that Action delegate encapsulates a method that does not have any parameter and returns void.
If we want to encapsulate a method having a parameter then we should use Action<T>. In the code which I am sharing, the method is having int parameter. And I am using Action delegate instead of Action<T>. Still I am able to invoke the method.
I am not able to understand this. Could anyone please explain the reason?
public static void Fourth(Action action)
{
Console.WriteLine("Fourth invoked");
action();
}
public static void Third(int x)
{
Console.WriteLine("Third invoked");
int result;
result = 3 + x;
Console.WriteLine(result);
}
Action a2 = () => Third(50);
Fourth(a2);
This line Action a2 = () => Third(50); creates a new parameter less method, which executes the Third() method.
If you want to assign the Third method to a2. You should write Action<int> a2 = Third;. Which will result in calling a2(50);
It's easy to get confused here.
a2 is not encapsulating the method Third. It uses an anonymous method declared via your lambda
() => Third(50);
This is converted by the compiler to a method in your class like that:
private static void SomeSpecialName() // so special that the name can't be used in C#, hence anonymous
{
Third(50);
}
and it is that SomeSpecialName() (having no arguments) method that a2 now refers to.
I have a bunch of methods that return List of GridTiles, for example GetTopNeighbour. I would like to be able to pass them to method AutoConnect using the GetNeighboursHandler delegate as a parameter.
public delegate List<GridTile> GetNeighboursHandler(GridTile c);
public List<GridTile> GetTopNeighbour(GridTile c)
{
//do stuff and return list
return null;
}
public GridTile AutoConnect(GridTile c, GetNeighboursHandler del)
{
List<GridTile> tempList = del(c);
// do stuff with the tempList
}
public void Test(GridTile c)
{
AutoConnect(c, GetTopNeighbour(c));
}
In the Test method I get the error: ... cannot convert ...Generic.List...to GetNeighboursHandler.
Have I completely misunderstood how delegates work?
You need to pass a delegate (which is an object that knows how to call the method, ie: it holds the reference of a method)
What you have done is passing the function result that you get after it's execution
GetTopNeighbour(c) returns a List<GridTile>, and you are passing this return value in your code here
AutoConnect(c, GetTopNeighbour(c));
Instead you should pass the reference to that method GetTopNeighbour
AutoConnect(c, GetTopNeighbour);
Refer these This is a tutorial and here's another one
You have to pass the method (or rather, method group) itself, instead of calling it:
AutoConnect(c, GetTopNeighbour);
You're passing the result of GetTopNeighbour(c), which is a List<GridTile>, as a parameter to AutoConnect.
Instead, you want to pass the MethodGroup to be converted to a delegate, like so:
AutoConnect(c, GetTopNeighbour);
I'm having a problem that I can't seem to figure out, although its kind of a standard question here on StackOverflow.
base.Invoke(() => prgWork.Value = 100);
What am I doing wrong?
When I compile it I get the error message:
Cannot convert lambda expression to type delegate because delegate is not a type
Invoke takes a Delegate, rather than a typed Delegate to make the method as generic as possible. The problem is that type Delegate is an abstract type, and in order for the compiler to infer the number and Types of arguments or the return type of your lambda, you must provide a "concrete" Delegate type. A common solution to this is to wrap your lambda in an Action delegate, as Action derives from Delegate. In fact, you can pass any type that inherits from Delegate, directly or indirectly, including custom delegates you define in your project.
Try something like this:
base.Invoke(new Action(() => { prgWork.Value = 100; }));
If you find yourself using Invoke often, I would suggest creating an extension method such as this:
public static void Invoke(this Control control, Action action)
{
control.Invoke(action);
}
Or with C#6 syntax
public static void Invoke(this Control control, Action action) => control?.Invoke(action);
And now you will be able to use it like this:
someControl.Invoke(() => { prgWork.Value = 100; });
Here the compiler will create a new Action object for you, cleaning up your code. This also sheds some light on why your attempt did not work. You were basically asking the compiler to create an instance of the abstract class Delegate.
Try this:
base.Invoke((Action)(() => { prgWork.Value = 100; }));
I can call a non-void function from a lambda that's assigned to an Action (which expects a void return type):
static bool foo() { return true; }
...
// Action action = foo; // This gives a compile error though.
Action action = ()=>foo(); // This compiles
Initially I noticed this while looking at an example of List.ForEach(). I could execute a non-void function from the lambda and it compiled.
At first this seemed counter-intuitive, and made me think you can assign non-void functions to a void delegate (void acting like a sort of contravariant), but Action action = foo; didn't work.
So now I assume what's happening is that the lambda becomes void because that's the expected return type, so it discards the return type of the non-void function it calls. Is that correct?
Your example
Action action = ()=>foo();
is just like any other method:
public void MyTestMethod()
{
// do something
foo();
// do some more
}
This is totally legal in C# (though some static analyzers would warn you that you don't use foo()'s return value).
THe second example:
action = foo;
does not work, because foo itself is a method with a return type and therefor can only be assigned to a variable of type Func<bool>, but not to Action. The lambda () => foo() on the other hand has no return type and is a valid Action.
Well, in this line of code :
Action action = ()=> foo(); // This compiles
You actually create another method that runs your function using closure :
void AnonymousMethod()
{
foo();
}
To assign another method to a delegate that invokes method foo is not the same as assign foo method to delegate directly. And that's why line of code :
Action action = foo;
will not compile, because Action delegate has return type of void and method foo doesn't.
Here's my attempt at calling an existing generic method with a type argument. 'Strongly typed reflection' may not be a suitable term, but it basically means finding and invoking the reflected method without using a name string.
public class TestClass
{
public static void Test(Type type)
{
InvokeTestMethodWithType(type);
}
private void Test<T>() { ... }
private static void InvokeTestMethodWithType(Type type)
{
// This doesn't compile! - can I find Test<> using this approach?
Expression<Func<TestClass, Action>> ex = x => x.Test<>;
// invoke it
((MethodCallExpression)ex.Body).Method.MakeGenericMethod(type).Invoke(new TestClass(), null);
}
}
Sample call would end up call the private Test().
TestClass.Test(typeof(Foo))
As you can see, I'm struggling with the expression and not entirely sure if it can be executed in this manner.
Do I have to dummy invoke the action in the expression like this post?
x => x.Test<object>()
The trick I use is simple: pass a fake generic type argument:
Expression<Func<TestClass, WhateverTestReturns>> ex = x => x.Test<string>();
// invoke it
((MethodCallExpression)ex.Body)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(type)
.Invoke(new TestClass(), null);
The method call expression will then contain a method info for Test<string>(), but you can easily use GetGenericMethodDefinition to remove the generic argument, and then MakeGenericMethod to put a different one back in its place.
You don't even need to use Expression in a case like this - simply cast TestClass.Test<string> to a delegate, and you'll have a Method property that gives you the same method info.