Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 929
  • Last Modified:

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
0
townsma
Asked:
townsma
  • 6
  • 4
  • 3
1 Solution
 
AndyAinscowFreelance programmer / ConsultantCommented:
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.
0
 
townsmaAuthor Commented:
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..
0
 
AndyAinscowFreelance programmer / ConsultantCommented:

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
0
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
käµfm³d 👽Commented:
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.
0
 
AndyAinscowFreelance programmer / ConsultantCommented:
@kaufmed - point taken, I should have used void instead of null as well in the wording.
0
 
käµfm³d 👽Commented:
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>.
0
 
townsmaAuthor Commented:
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.
0
 
townsmaAuthor Commented:
This morning, after writing the above, I had a flash of inspiration, and worked out where I was going wrong.  I was assuming the _dialogResult  was the value being returned, but actually it was only the name of the delegate.

Once I realised this, I worked out, that the value in the lambda in the calling method, instead of being dialogResult, it should be (boolVal, stringVal) => {...}

Once I did this all worked as expected, and the calling method gets the two values I required to be passed back.

I have never used this before, but it is actually a very useful thing.
0
 
AndyAinscowFreelance programmer / ConsultantCommented:
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.
0
 
townsmaAuthor Commented:
I will put together a code snippet that shows all the relative bits, and post it on here for others.
0
 
townsmaAuthor Commented:
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.
0
 
käµfm³d 👽Commented:
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.
0
 
townsmaAuthor Commented:
I ultimately found the solution myself.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 6
  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now