Link to home
Start Free TrialLog in
Avatar of townsma
townsmaFlag for Indonesia

asked on

Using Action delegate with multiple return values.

I have recently been introduced to System.Action returns from dialog forms.  When return a single value, i.e. Action<Boolean> or Action<String> they work well.  

However, I am now trying to create an Action<Bool, String>, which according to the Action documentation is perfectly legal.

But when I do this I get an error saying incompatible anonymous function signature, and there appears to be no way to recover the values.

The constructor in the dialog is as follows.

 private readonly Action<bool, String> _dialogAction;

        public GeneralTextDialogFragment(Activity context, Action<bool, String> dialogResult, InputTypes inputType)
        {
            _context = context;
            _dialogAction = dialogResult;
            ValueType = inputType;
        }

Open in new window


And the calling code is

var dialog = new Fragments.GeneralTextDialogFragment(this, dialogResult =>
                                                                               {
                                                                                   var result = dialogResult;

                                                                                   // other stuff goes here.
                                                                                        
                                                                                        
                                                                                    }, InputTypes.ClassNumber);

Open in new window


But the dialogResult parameter is red underlined, and the result value, I cannot see the two values.

All of this code works perfectly if I select Action<Boolean> or Action<String>.

Can someone please point out where I am going wrong.

Please note this code is being used in Xamarin (Mono) for Android.

Thanks
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

I have recently been introduced to System.Action returns from dialog forms.  When return a single value, i.e. Action<Boolean> or Action<String> they work well.  

However, I am now trying to create an Action<Bool, String>, which according to the Action documentation is perfectly legal.



Erm, according to the documentation I see the System.Action can only return void.  What is in the listing are the parameters being passed into the function, not what is being returned out of the function.
Avatar of townsma

ASKER

I believe the action can be used for values in and out.  This is backed up by the fact  I am already passing out a Boolean in one instance, and a String in a different instance using action, and both of these work, and return the correct values..

1.

void foo(int)  //returns null, takes one int as input

2.

int foo(void)  //returns int, takes no input

3.

int foo(bool, string)  //returns int, takes two bool, string as input

4.

bool, string foo(int)  //returns two bool and string, takes one int as input
To the best of my knowledge 1, 2 and 3 but not 4 are correct function declarations.
The System.Action would however only allow 1 as a valid action (according to my documentation).

Can you post a link to where you have this documentation that all 4 of the above are valid for System.Action
I believe the action can be used for values in and out
Action does not permit a return value, as Andy mentions. Func might be what you are thinking of. It's the equivalent of Action, but with return values. One thing to note (that I struggled with--for some weird reason--when I first started working with Func):  The last type listed in the type list for Func will always be the type of the return value; everything else is a type corresponding to a parameter.

As to your error, your lambda does not accept two parameters, which your Action definition states.


@AndyAinscow

void foo(int)  //returns null, takes one int as input

I would rephrase that as:

void foo(int)  //Doesn't return anything, takes one int as input

"Returns null" implies that it has a return value, since you can return null from certain functions.
@kaufmed - point taken, I should have used void instead of null as well in the wording.
I missed the Mono mention earlier, but just for completeness, from http://docs.go-mono.com/?link=T%3aSystem.Action%3CT%3E:

A delegate to a method that returns no values, and takes one parameter.

I just wanted to confirm that Mono wasn't doing things differently than .NET, and it doesn't appear to be by the documentation. Unforunately, Action<T1, T2> isn't documented yet, but I have no reason to suspect it works any differently than Action<T1>.
Avatar of townsma

ASKER

Thanks for this gents, but this is where I am getting confused.  Whilst I understand what you are saying, I am confused by the direction of things.

In the working examples given to me, which as I said work, the values for T1, and for that matter T2, T3, etc. are set in the dialog form, not in the calling form, hence my understanding they are used to pass values back to the calling method.

In my code examples above you will see the action, _dialogResult, is defined in the form.  Later in the code, not shown in the example, we set the values to both the string and the boolean, which it takes without error.  The same way as if I was only passing a string or boolean.

We then actually read these values, or at least we can when it is a single string or boolean, in the lambda expression associated with the call to the dialog form.

At no point do we load any values in the dialogResult in the calling form, in fact I do not even know how to do that.  

I think I will have to go back to the guy who showed me about Actions originally, maybe he has a better understanding of how they work.
ASKER CERTIFIED SOLUTION
Avatar of townsma
townsma
Flag of Indonesia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Could you please post the complete piece of code or at least a link to the documentation.
Returning multiple values is a relatively common question.
Avatar of townsma

ASKER

I will put together a code snippet that shows all the relative bits, and post it on here for others.
Avatar of townsma

ASKER

For anyone who is interested how I got two values backup from a DialogFragment, my code is below.  I am sure better developers than I could improve on this, but it works for me. ;-)

The dialog fragment, contains a label TextView, a string entry box TextEdit, and an ok and cancel button.  The idea being I can display this, tell the user what data I wanted, and let him select ok or cancel.  The dialog will return both the sting value, and a boolean for is ok was pressed.

public class GeneralTextDialogFragment : DialogFragment
    {
        private EditText _txtAnswer;

        private readonly String _question;

        private readonly InputTypes _valueType;

        private Activity _context;
        private readonly Action<bool, String> _dialogAction;

        public GeneralTextDialogFragment(Activity context, InputTypes inputType, String message, Action<bool, String> dialogResult)
        {
            _context = context;
            _dialogAction = dialogResult;
            _valueType = inputType;
            _question = message;
        }

        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
        }

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var view = inflater.Inflate(Resource.Layout.dialog_text, container);
            _txtAnswer = view.FindViewById<EditText>(Resource.Id.txt_answer);

            _txtAnswer.InputType = _valueType;

            if (_valueType == InputTypes.ClassText)
            {
                _txtAnswer.InputType  = InputTypes.ClassText | InputTypes.TextFlagCapWords | InputTypes.TextVariationLongMessage;
            }

            _txtAnswer.EditorAction += (sender, args) =>
                                           {
                                               if (args.ActionId == ImeAction.Done)
                                               {
                                                   if (null != _dialogAction)
                                                   {
                                                       _dialogAction(true, _txtAnswer.Text);
                                                   }
                                                   Dismiss();
                                                   args.Handled = true;
                                               }
                                               else
                                               {
                                                   if (null != _dialogAction)
                                                   {
                                                       _dialogAction(false, String.Empty);
                                                   }
                                                   Dismiss();
                                                   args.Handled = true;
                                               }
                                           };
            Dialog.SetTitle(_question);

            _txtAnswer.RequestFocus();
            Dialog.Window.SetSoftInputMode(SoftInput.StateVisible);

            return view;
        }
    }

Open in new window


Then in the main program, we call this diaglog, pass the question, and process the results. i.e.

var transaction = FragmentManager.BeginTransaction();

var dialog = new Fragments.GeneralTextDialogFragment(this, InputTypes.ClassText, Resources.GetString(Resource.String.guest_name), (result, stringValue) =>
                                                                               {
                                                                                   if(result && !String.IsNullOrEmpty(stringValue))
                                                                                        Invoice.CustomerName = stringValue;
                                                                                   else
                                                                                       isCancel = true;
                                                                               });

                
                dialog.Show(transaction, "fragment_get_name");

Open in new window


I hope other found this helpful.  

To be honest, most of this came from different posts on the internet, but it took a few days research, and lots of trial and error to get here.  So to all those who made the original posts I borrowed from, thanks.
You're not returning anything from that lambda (I don't see a return statement; do you?). You're simply creating a closure around variables that exist outside of the lambda (but still in the same scope as the lambda itself's definition). Read up on closures to see why this happens.
Avatar of townsma

ASKER

I ultimately found the solution myself.