Solved

Using Action delegate with multiple return values.

Posted on 2013-10-28
13
731 Views
Last Modified: 2016-02-19
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
Comment
Question by:townsma
  • 6
  • 4
  • 3
13 Comments
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39605207
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
 
LVL 6

Author Comment

by:townsma
ID: 39605217
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39605257

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
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39605813
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39605908
@kaufmed - point taken, I should have used void instead of null as well in the wording.
0
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39606385
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
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 6

Author Comment

by:townsma
ID: 39607564
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
 
LVL 6

Accepted Solution

by:
townsma earned 0 total points
ID: 39607609
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
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 39607891
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
 
LVL 6

Author Comment

by:townsma
ID: 39607904
I will put together a code snippet that shows all the relative bits, and post it on here for others.
0
 
LVL 6

Author Comment

by:townsma
ID: 39608053
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
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39608481
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
 
LVL 6

Author Closing Comment

by:townsma
ID: 39618585
I ultimately found the solution myself.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Displaying an arrayList in a listView using the default adapter is rarely the best solution. To get full control of your display data, and to be able to refresh it after editing, requires the use of a custom adapter.
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
This video is in connection to the article "The case of a missing mobile phone (https://www.experts-exchange.com/articles/28474/The-Case-of-a-Missing-Mobile-Phone.html)". It will help one to understand clearly the steps to track a lost android phone.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now