3

I'm looking to create a Button class in my custom XNA GUI that accepts methods as an argument, similar to how, in Python's tkinter you can just set the function to be called with Button.config(command = a_method) .

I've read about using delegates as parameters here, here and here, but I don't seem to be any closer to getting it working. I don't fully understand how delegates work, but I've tried several different things unsuccessfully, like using Func<int>? command = null in order to test later to see if command is null then I'd call the preset default, but then I get a Func cannot be nullable type or something similar.

Ideally the code'd be something like:

class Button
{
//Don't know what to put instead of Func
Func command;

// accepts an argument that will be stored for an OnClick event
public Button(Action command = DefaultMethod)
  {
    if (command != DefaultMethod)
    {
       this.command = command;
    }
  }
}

But it seems like everything I've tried is not working out.

Cœur
  • 37,241
  • 25
  • 195
  • 267
TankorSmash
  • 12,186
  • 6
  • 68
  • 106
  • What UI framework? Winforms? WPF? – CodingGorilla Aug 23 '12 at 18:36
  • 3
    `Func command = null` should work fine. `Func` is a reference type, so can't be nullable. – Oded Aug 23 '12 at 18:36
  • 1
    @CodingGorilla - does it matter? – Oded Aug 23 '12 at 18:38
  • @CodingGorilla It's going to be an XNA custom GUI – TankorSmash Aug 23 '12 at 18:39
  • Are you trying to figure out a data type that can store any kind of delegate? That's `Delegate` - but I'm not sure I understand why/how this delegate will by used, what would the `int` return value of a `Func` represent for a button push? Seems like just `Action` would be fine for both the parm & the internal type. If that's the parameter for the contstructor, why do you need something different to store it in a property. – Jamie Treworgy Aug 23 '12 at 18:42
  • @Oded Only in that if he was using WPF I was going to suggest he can use `ICommand`. – CodingGorilla Aug 23 '12 at 18:42
  • @jamietre It actually does seem like `Action` would be a better choice. It was `int` only because I hadn't figured out what `Action` was, and `void` was not a valid choice. – TankorSmash Aug 23 '12 at 18:44
  • If there's never any need for a return value, or a delegate with parameters in this situaion, just use `Action`. `Action` will actually accept a delegate for something with no parameters that has a return value, too, or you can wrap a more complex function signature in an inline function if needed to pass in to your class. – Jamie Treworgy Aug 23 '12 at 18:45

3 Answers3

1

Default parameters must be a compile time constant. In C#, Delegates can't be constants. You can achieve a similar result by providing your own default in the implementation. (just using Winforms here)

    private void button1_Click(object sender, EventArgs e)
    {
        Button(new Action(Print));
        Button();
    }

    public void Button(Action command = null)
    {
        if (command == null)
        {
            command = DefaultMethod;
        }
        command.Invoke();
    }

    private void DefaultMethod()
    {
        MessageBox.Show("default");
    }

    private void Print()
    {
        MessageBox.Show("printed");
    }
P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
  • I'm not 100% sure, but maybe 93.2% sure, that `command()` will be a lot faster than `command.Invoke()` since the latter uses reflection. But either way it's not necessary with strongly typed delegates to use `Invoke`, you can just pass parameters (or no parameters) using regular function syntax. – Jamie Treworgy Aug 23 '12 at 19:11
  • @jamietre That's just syntactic sugar. It makes no difference. – P.Brian.Mackey Aug 23 '12 at 19:15
  • 2
    Stupid 6.8%, always making me look bad. – Jamie Treworgy Aug 23 '12 at 19:16
0

If you are interested in a default value, would something like this work?

class Button
{
  //Don't know what to put instead of Func
  private readonly Func defaultMethod = ""?
  Func command;

  // accepts an argument that will be stored for an OnClick event
  public Button(Action command)
  {
    if (command != defaultMethod)
    {
       this.command = command;
    }
  }
}
  • Hmm, this does seem like a good option too, but Oded's answer works really well. http://stackoverflow.com/questions/12097989/using-methods-as-a-default-parameter#comment16167961_12097989 Thank you – TankorSmash Aug 23 '12 at 18:47
  • You're really good at linking to posts and comments. Is there a section on how to do that in the FAQ? ...and what's with the downvote? –  Aug 23 '12 at 18:48
  • 1
    Just upvoted you, so you're back at 0, dunno why you were at -1. If you click the time code of a answer/comment, you get a link to it. If you highlight text, and click the link button or hit `ctrl L` in the `ask a question` screen, you can make the highlighted text a link – TankorSmash Aug 23 '12 at 18:51
  • 1
    It wasn't me, but someone probably downvoted you because this code will not compile. There's no type `Func`, it requires a generic parameter, and you can't assign an `Action` to a `Func`. – Jamie Treworgy Aug 23 '12 at 19:14
0

The error you got about Func<T> not being nullable it right - it is a reference type and only value types can be nullable.

To default a Func<T> parameter to null, you can simply write:

Func<int> command = null
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Is there a way to have the `` type be more dynamic? After playing with the code a bit, I've realize that it won't always be an `int` returned, and sometimes there won't even be a returned value in some of the functions I'd like to pass. – TankorSmash Aug 23 '12 at 19:33
  • @TankorSmash - You can keep it generic, with generic type constraints, but this will not let you limit yourself to numeric types and will require making the class generic. – Oded Aug 23 '12 at 19:37