How to return value from Action()?
C#.NetLinqC# 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:
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)
);