2

I'd like to do something like this, but it's not possible.(Cann't convert from 'void' to 'System.Action').

class Program
{
    public static void Main()
    {
        int n = 2;
        ClassB cb = new ClassB();

        cb.SetMethod(ClassA.MethodA(n)); //Cann't convert 'void' to 'System.Action<int>'

    }
}

public class ClassA
{
    public static void MethodA(int a)
    {
        //code
    }
}

public class ClassB
{
    Delegate del;
    public void SetMethod(Action<int> action)
    {
        del = new Delegate(action);
    }
    public void ButtonClick()
    {
        del.Invoke();
    }
}
public delegate void Delegate(int n);

I can send the argument "n", as second argument in the "setMethod" method, but I would have to store a variable to after pass to "del.Invoke(PARAM)". I'd like to use "del.Invoke()".

3
  • setMethod has one Action parameter and methodA returns the void parameter, so you have to change the return type of methodA to Action<int> Commented Jun 15, 2017 at 5:28
  • @StuartLC even then this code still shouldn't work, the ClassB.ButtonClick method is calling invoke on a single param action, but not providing a param. It's a little unclear precisely what they're trying to achieve here... Maybe they just want an Action with no arguments so: setMethod(() => classA.methodA(n) as you mentioned? Commented Jun 15, 2017 at 5:32
  • Please, follow C# naming guidelines. This little detail makes your code unreadable. Methods should start from uppercase letter. Commented Jun 15, 2017 at 5:41

3 Answers 3

2

You seem to have a misunderstanding of delegates. Delegates represent methods, not method calls. If you supply arguments to a method, it becomes a method call. So here:

cb.setMethod(ClassA.methodA(n));

ClassA.methodA(n) is a method call, and you can't assign that to a delegate.

Basically, you can't pass the parameter at this stage. You have to pass the parameter when you invoke the delegate. e.g.

del.Invoke(5);

But you said you want to always write del.Invoke(), with no arguments. Well, then you should not use an Action<int>, you should just use Action, which does not accept any parameters.

class Program
{
    public static void Main()
    {
        int n = 2;
        ClassB cb = new ClassB();

        cb.setMethod(() => ClassA.methodA(n));

    }
}

public class ClassA
{
    public static void methodA(int a)
    {
        //code
    }
}

public class ClassB
{
    Delegate del;
    public void setMethod(Action action)
    {
        del = new Delegate(action);
    }
    public void ButtonClick()
    {
        del.Invoke();
    }
}
public delegate void Delegate();
Sign up to request clarification or add additional context in comments.

2 Comments

This works fine. Maybe I have a misunderstanding of delegates. Do you think it's a bad practice ? Can you recommend other way or classes to do something like these?
@eag845 It's hard to say whether this is bad practice or not without context. This per se is okay, I think. If you think my answer answers your question, please consider accepting it by clicking on that checkmark!
0

cb.setMethod(new Action(ClassA.methodA));

Comments

0

It isn't clear whether you want to capture the integer at the call site (e.g. as a closure), or whether you intend passing in a parameter explicitly to the delegate.

Here's the former case, where the value is captured:

public static void Main()
{
    var n = 2;
    var cb = new ClassB();
    cb.setMethod(() => ClassA.methodA(n));
}

The delegate is thus unaware of the captured variable, and is just defined as:

public delegate void Delegate();

If however you do intend passing the int at invoke time, then the value for the int needs to be passed in the ButtonClick:

public static void Main()
{
    var cb = new ClassB();
    cb.setMethod(ClassA.methodA);
}
public class ClassB
{
    Delegate del;
    public void setMethod(Action<int> action)
    {
        del = new Delegate(action);
    }
    public void ButtonClick()
    {
        var n = 2;
        del.Invoke(n);
    }
}
public delegate void Delegate(int n);

Edit - Re Do you think there's a better way

There's no real reason to explicitly require a delegate. Action and Func (and Action<int>, depending on the above) are already delegates. As an improvement, you should check that the action is assigned before invoking it. The null conditional operator will simplify this as _action?.Invoke(). But you can go one step further, and prevent the action from ever being unassigned by requiring it in the constructor:

public class ClassB
{
    // Can be readonly if it is assigned only ever once, in the ctor.
    private readonly Action _action;

    public ClassB(Action action)
    {
        Contract.Assert(action != null);
        _action = action;
    }

    public void ButtonClick()
    {
        _action(); // i.e. no need for Invoke or null check.
    }
}

2 Comments

cb.setMethod(() => ClassA.methodA(n)) Works fine =) . In the last case you wrote, I think I don't need Delegate too. But In my case, Do you think there's a better way ?
Yes, that captures the value of n as it was when the setMethod subscription was set up. I've added some minor ideas about improving the safety of ClassB.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.