How to return value from Action()?

C#.NetLinq

C# Problem Overview


In regards to the answer for this question https://stackoverflow.com/questions/8098950/passing-datacontext-into-action, how do I return a value from action(db)?

SimpleUsing.DoUsing(db => { 
// do whatever with db 
}); 

Should be more like:

MyType myType = SimpleUsing.DoUsing<MyType>(db => { 
// do whatever with db.  query buit using db returns MyType.
}); 

C# Solutions


Solution 1 - C#

You can use Func<T, TResult> generic delegate. (See MSDN)

Func<MyType, ReturnType> func = (db) => { return new MyType(); }

Also there are useful generic delegates which considers a return value:

  • Converter<TInput, TOutput> (MSDN)
  • Predicate<TInput> - always return bool (MSDN)

Method:

public MyType SimpleUsing.DoUsing<MyType>(Func<TInput, MyType> myTypeFactory)

Generic delegate:

Func<InputArgumentType, MyType> createInstance = db => return new MyType();

Execute:

MyType myTypeInstance = SimpleUsing.DoUsing(
                            createInstance(new InputArgumentType()));

OR explicitly:

MyType myTypeInstance = SimpleUsing.DoUsing(db => return new MyType());

Solution 2 - C#

Your static method should go from:

public static class SimpleUsing
{
    public static void DoUsing(Action<MyDataContext> action)
    {
        using (MyDataContext db = new MyDataContext())
           action(db);
    }
}

To:

public static class SimpleUsing
{
    public static TResult DoUsing<TResult>(Func<MyDataContext, TResult> action)
    {
        using (MyDataContext db = new MyDataContext())
           return action(db);
    }
}

This answer grew out of comments so I could provide code. For a complete elaboration, please see @sll's answer below.

Solution 3 - C#

Use Func<T> rather than Action<T>.

Action<T> acts like a void method with parameter of type T, while Func<T> works like a function with no parameters and which returns an object of type T.

If you wish to give parameters to your function, use Func<TParameter1, TParameter2, ..., TReturn>.

Solution 4 - C#

You can also take advantage of the fact that a lambda or anonymous method can close over variables in its enclosing scope.

MyType result;

SimpleUsing.DoUsing(db => 
{
  result = db.SomeQuery(); //whatever returns the MyType result
}); 

//do something with result

Solution 5 - C#

In addition to ssls answer: For a software we call a lot of "micro methods" to query this and that from the local machine. Sometimes exceptions appear (file / folder not existing, etc). In order to not repeat our self over and over with try/catch blocks, we used ssls approach with return values:

private T ExecuteAndCatch<T>(Func<T> action, T defaultReturn)
{
    try
    {
        return action();
    }
    catch (Exception ex)
    {
        Log.e("Exception during ExecuteAndCatch", ex);
        return defaultReturn;
    }
}

Example of mentioned micro-methods:

private Boolean CheckKasperskyInstalled() => System.IO.File.Exists(Environment.ExpandEnvironmentVariables(@"%pf_x86%\Kaspersky Lab\Kaspersky Endpoint Security for Windows\avp.exe"));
private Boolean CheckKasperskyRunning() => System.Diagnostics.Process.GetProcessesByName("avp").Length > 0;

And when using, we no longer have to care if these Methods might throw an exception for whatever reason:

cci = new ComputerComplianceInfo();
cci.KasperskyInstalled = ExecuteAndCatch(() => CheckKasperskyInstalled(), false);
cci.KasperskyRunning = ExecuteAndCatch(() => CheckKasperskyRunning(), false);

Solution 6 - C#

I had this need too, but involving cross-thread concerns - Method in MainForm returning data out of a control in an UserControl. Original code was an extension method that perform an Action; later, I needed to return a value, and as @sll suggested, had to use the Func delegate instead.

In a ControlExtensions class:

     public static TResult RunOnUiThread<TResult>(this Control @this, Func<TResult> code)
        {
            if (@this.InvokeRequired)
            {
                var toReturn = @this.BeginInvoke(code);                  
                return (TResult)@this.EndInvoke(toReturn);
            }
            else
            {
                return code.Invoke();
            }
       }

This extension method is called from a method in MainForm, getting some value from a control inside an UserControl; in this example, I'm returning the value of the SelectedItem from a ListBox:

                return UserControl1.listBox1.RunOnUiThread(
                    () => UserControl1.listBox1.GetItemText(
                            UserControl1.listBox1.SelectedItem)
                    );

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question4thSpaceView Question on Stackoverflow
Solution 1 - C#sllView Answer on Stackoverflow
Solution 2 - C#Kirk WollView Answer on Stackoverflow
Solution 3 - C#Ulysses AlvesView Answer on Stackoverflow
Solution 4 - C#Steve RowbothamView Answer on Stackoverflow
Solution 5 - C#dognoseView Answer on Stackoverflow
Solution 6 - C#Marcelo Scofano DinizView Answer on Stackoverflow