Parameterless anonymous method can be assigned to parametered delegate - c#

class Program{
static void Main(){
test11 jhbee = Program.test; //error
test11 yep = delegate { }; //no error
}
static void test() { }
}
delegate void test11(int r);
So I have the delegate test11 which returns void and takes 1 parameter.
whn I try to assign a parameterless method to an instance of test11, the compiler, obviously issues an error but when I assign a parameterless anonymous method to an instance of test11, no error is issued.
I mean, I can see that there is no obstacle for it to work but can you please tell me why is this so? Is there a good reason for this?

From the documentation:
When you use the delegate operator, you might omit the parameter list. If you do that, the created anonymous method can be converted to a delegate type with any list of parameters
So while it appears that way, the created anonymous method is not really parameterless - its parameter list has just not been defined.
As for the reason why this is an option, this answer to a related question might help explain it.

Related

Is there a base class of all delegate in C#

Is there a way to pass different methods as parameter into another method in C#? I have some code like this below
class Program
{
void Action1(string s){...}
string Func1(string s){...}
void RegisterAction(Action<string> action){...}
void RegisterFunc(Func<string, string> func){...}
void Register(????){ }
void Run()
{
RegisterAction(Action1); // ok
RegisterFunc(Func1); // ok
Register(Action1); // ?
Register(Func1); //?
}
}
I want to pass Action1 and Func1 into the same method 'Register', in which I can use reflection to implement my logic. but the Action<string>, Func<string,string>are different delegates, perhaps I will have to deal with Action<int>, Func<object> and many other types I don't want to write the 'register' methods for every action and function.
Any help? Thank you.
There is a base type - MulticastDelegate which itself has a base type of Delegate - but the problem is that you're relying on a method group conversion to a delegate type, and that requires a specific delegate type to convert to. Register(Action1) could convert the method group Action1 to any delegate type returning void and accepting a string - there can be multiple such delegates. How would the compiler know which one you mean?
What you could do is keep your RegisterAction and RegisterFunc methods, but then use a common implementation which stores the delegates in (say) a List<Delegate>.

Why is it legal to invoke an Action without have first assigned it to anything?

My understanding of Actions in C# is that they are just a specific version of a delegate, namely one with no parameters and no return type.
If I create a class like this...
class TrainSignal
{
public delegate void TrainsAComing();
public void HerComesATrain()
{
TrainsAComing();
}
}
...it won't compile since I haven't created an instance of the delegate. However if I replace the delegate definition with that of an Action like below it compiles:
class TrainSignal
{
public Action TrainsAComing;
public void HerComesATrain()
{
TrainsAComing();
}
}
I thought perhaps that maybe an Action is static or something like that (thereby allowing us to call it by it's name without instantiating it) but that doesn't seem to be the case.
Can anyone explain why the second code block is legal?
This line
public delegate void TrainsAComing();
defines a public delegate type called TrainsAComing, nested inside your class. This would let users create delegates of type TrainSignal.TrainsAComing, but TrainSignal would have no member to store an instance of such delegate.
In other words, declaration of the delegate does not define a member of delegate type. You need another declaration for that:
class TrainSignal
{
public delegate void TrainsAComing(); // The delegate type
public TrainsAComing OnTrainsAComing; // The member of delegate type
public void HerComesATrain()
{
OnTrainsAComing();
}
}
Action, on the other hand, is already a type, similar to delegate TrainsAComing from your example. Hence, the definition
public Action TrainsAComing;
makes TrainsAComing a member of TrainSignal capable of storing a delegate.
An Action field is a field like any other. It's a ref type, so the field is null initialized. You will receive a NullReferenceException. This is perfectly safe but not useful.
Maybe you meant to actually reference a function?
Action TrainsAComing = () => Console.WriteLine("...");
Or maybe, the confusion comes from the fact that the first code snippet declares a delegate type and the second one declares a field of a delegate type.

Cannot convert method group '' to non-delegate type 'System.Delegate'. Did you intend to invoke the method?

I'm trying to store a function reference in a Delegate type for later use.
Here's what I'm doing:
class Program
{
static void Test()
{
}
static void Main(string[] args)
{
Delegate t= (Delegate)Test;
}
}
In this I'm getting following error:
Cannot convert method group 'Test' to non-delegate type 'System.Delegate'.
Did you intend to invoke the method?
Why is this happening?
You really shouldn't ever use the type Delegate to store a delegate. You should be using a specific type of delegate.
In almost all cases you can use Action or Func as your delegate type. In this case, Action is appropriate:
class Program
{
static void Test()
{
}
static void Main(string[] args)
{
Action action = Test;
action();
}
}
You can technically get an instance of Delegate by doing this:
Delegate d = (Action)Test;
But actually using a Delegate, as opposed to an actual specific type of delegate, such as Action, will be hard, since the compiler will no longer know what the signature of the method is, so it doesn't know what parameters should be passed to it.
What you are trying to do here is cast the method group Test to something. As per the spec, the only legal cast for a method group is casting it into a delegate type. This can be done either explicitly:
var t = (Delegate)Test;
or implicitly:
Delegate t = Test;
However, as the documentation says, System.Delegate itself is... not a delegate type:
The Delegate class is the base class for delegate types. However, only
the system and compilers can derive explicitly from the Delegate class
or from the MulticastDelegate class. It is also not permissible to
derive a new type from a delegate type. The Delegate class is not
considered a delegate type; it is a class used to derive delegate
types.
The compiler detects this and complains.
If you want to cast a method group to a delegate you will have to specify a delegate type with a compatible signature (in this case, Action).

What makes a name of a method equivalent to an action delegate?

I was just experimenting and ended up with the following snippet:
public static class Flow {
public static void Sequence(params Action[] steps) {
foreach (var step in steps)
step();
}
}
void Main() {
Flow.Sequence(() => F1(), () => F2());
Flow.Sequence(F1, F2); // <-- what makes this equiv to the line above?
}
void F1() { }
void F2() { }
I didn't realize that a method name alone was the same as an Action.
What is making this so?
In C#, delegates are nothing more than method pointers. They can point to existing methods in a class, or independent anonymous delegate objects altogether.
This paragraph from the above link should explain what's happening in your code:
Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.
That is, when resolving delegate types, what's considered are their signatures, rather than their names.
In your case, your F1() and F2() methods, taking no parameters and returning nothing, have matching signatures with the parameterless Action delegate:
public delegate void Action();
Therefore, they're implicitly convertible to Action.
If you try to pass a method with a different return type or at least one parameter, you'll get a compile-time error as it won't correspond to Action's signature.
Basically, this is kind of what is happening in the background:
void Main()
{   
Flow.Sequence(new Action(delegate(){ F1(); }), new Action(delegate(){ F2(); }));   
Flow.Sequence(new Action(F1), new Action(F2));
}
They're not EXACTLY equivalent, but they're very close. They would render the same results at run-time, the only difference being that the arguments in the first Sequence invocation would be an Action which invokes an anonymous method which then invokes the static methods F1 and F2; the second Sequence invocation would be an Action which invokes the static methods F1 and F2.
I hope this helps.
The compiler uses an implicit conversion from a method group to a delegate of compatible type (in this case a void returning method taking no arguments), the method names here are irrelevent.

Missing assembly for Action<T>

What is the assembly to use with Action<T>? I get an error on T not being accepted, that an assembly or namespace is missing.
Method(delegate { OnChange(); });
private static void MethodUsingOtherMethod(Action<T> action)
{
//TODO
}
If I put an extra <T> after MethodUsingOtherMethod then T is accepted, but then the argument in the delegate above is not recognized.
I want to use OnChange() in the second method, to be called from there.
You have to declare your method as a generic method:
private static void MethodUsingOtherMethod<T>(Action<T> action)
{
//TODO
}
Edit: By the way, you only need to do this if you need the passed in delegate to take a parameter of type T. If you need a parameterless method to be passed in, define your method like this:
private static void MethodUsingOtherMethod(Action action)
{
//TODO
}
Action<T> means that you need to pass a delegate accepting 1 argument of type T. So, if you want to pass OnChange, then you can just specify Action without the T.
That way, your code would look like this:
MethodUsingOtherMethod(OnChange);
private static void MethodUsingOtherMethod(Action action)
{
//TODO
}
The error you got about T not being recognized is, because you didn't declare your method as a generic method (done by putting the <T> behind the method name). Because of that, the compiler didn't recognize T as a type argument and tried to look up a type called T, which doesn't exist.
You might want to look up Generics to understand what's going on there:
http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx

Categories