How to accept a code block on a method C# - c#

I'm writing an extensions library for C#, and I'm wondering if it's possible to accept code blocks on a method call. Something like below:
foo()
{
var bar = 0;
};
Or something like this would also do:
foo(
{
var bar = 0; //As an argument to the method
});

You can pass in a delegate/lambda to accomplish this.
public void foo(Action del)
{
var local = del;
if (local != null)
{
local();
}
}
foo(() =>
{
var bar = 0;
});
You can read more here

Related

Add methods to Func

I have a list of Func and I want to add elements.
If I add them on Start like below, no problem:
public List<System.Func<bool>> conditions = new List<System.Func<bool>>();
void Start()
{
conditions.Add(Iamdead);
conditions.Add(Iamalive);
}
bool Iamdead()
{
...
return ...;
}
bool Iamalive()
{
...
return ...;
}
But I want to define the list without Start so that I have a clean list of methods that I can see as elements in a row. I have tried the classic format:
public List<System.Func<bool>> conditions = new List<System.Func<bool>>()
{
bool Iamdead()
{
...
return ...;
}
,
bool Iamalive()
{
...
return ...;
}
};
This gave me parsing error
I tried like that:
public List<System.Func<bool>> conditions = new List<System.Func<bool>>()
{
Iamdead,Iamalive
};
static bool Iamdead()
{
...
return ...;
}
static bool Iamalive()
{
...
return ...;
}
This worked only if the methods are static but I do not want them to be static. Without static, it doesn't work. It seems I couldn't understand the data structure here. Can anyone tell me the correct way of defining Func in a list?
Thanks
I strongly suspect the problem is that you're trying to access this (implicitly) within a field initializer. You're not allowed to do that. Just move the initialization into a constructor:
// You don't really use public fields, do you?
private readonly List<Func<bool>> conditions;
public MyClass()
{
conditions = new List<Func<bool>> { Method1, Method2 };
}
private bool Method1() { ... }
private bool Method2() { ... }
(I'm assuming you actually want your conditions to depend on state within the instance. If they don't, you don't need this. If the methods aren't used other than for these conditions, and they're short enough, you might want to use lambda expressions instead.)
Like so:
public List<System.Func<bool>> conditions = new List<System.Func<bool>>()
{
() => false,
() => true,
};
You can just use lambdas:
public List<System.Func<bool>> conditions = new List<System.Func<bool>>()
{
() =>
{
return false;
}
,
() =>
{
return true;
}
};

Clarification on c# delegates / methods

I've used delegates in the past so I'm familiar with their use and benefits. I've also done a lot of reading/research, but I'm trying to wrap my head around this and getting nowhere. I'd like to use a delegate (I believe) to encapsulate some code or use a delegate within a method to call some outside code.
I'm using the same code in 20 different places to wrap an entity framework update in a transaction. I would like the code in one place; however, I can't do it in a method by itself because there is a middle part that will change each time. I'm looking for some ideas / clarification on how I can best do this (.net 3.5, ms sql 2010). - thnx
code sample:
void AddItem(string objDetails)
{
// setup method specific entity objects
SomeObject obj = new SomeObject { Details = objDetails };
//////// Begin transaction code that I would like to encapsulate ///////
bool success = false;
using (Entities data = new Entities())
{
for (int i = 0; i < s.BaseSettings.CommandRetries; i++)
{
using (TransactionScope transaction = new TransactionScope())
{
try
{
//////////////////////////////////////////////////////////////
///////// BEGIN Code that I would like to change / call different each time ////////
data.AddToSOMEOBJECTs(obj);
//////////////// END //////////////////////////////////
//// RETURN TO ENCAPSULATED CODE ////
data.SaveChanges(false);
transaction.Complete();
success = true;
break;
}
catch (Exception ex)
{
if (ex.GetType() != typeof(UpdateException))
{
throw new Exception("Unhandled db exception.");
}
}
}
}
if (success)
{
data.AcceptAllChanges();
}
else
{
throw new Exception();
}
}
}
You pass the function a delegate (or lambda) that does the custom bit
like this
void AddItem<T>(string objDetails, Func<T> custom) {
.
. common
.
.
T someReturn = custom();
.
. common
.
}
add call like this perhaps:
Func<int> custom = () => {
// do something custom
return 9;
}
// Call common function
AddItem<int>(..., custom);
All that matters is that the interface for the Func matches what you need.
You can pass different methods by using a delegate and an event. Here's an example of a class that does this:
class SampleClass
{
public delegate void TransactionDelegate();
public event TransactionDelegate MyTransactionDelegate;
public void DoSomething()
{
MyTransactionDelegate();
}
}
You can then use a lambda expression to pass methods to the event like this:
class MainClass
{
public static void Main (string[] args)
{
var test = new SampleClass();
test.MyTransactionDelegate += () => {Console.WriteLine("Success");};
test.DoSomething();
}
}

MethodInvoker + lambda + arguments + CrossThread Operation

I'm using this to change something on other thread:
MethodInvoker m = () => { login_submit.Text = "Login"; };
if (InvokeRequired)
{
BeginInvoke(m);
}
else
{
Invoke(m);
}
this is working fine.
How can I pass argumets to that lamba expression?
I want to do sth like that:
MethodInvoker m = (string txt) => { login_submit.Text = txt; };
if (InvokeRequired)
{
BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
Invoke(m); // I need to pass txt string in some way here.
}
If this is a common scenario for you, I suggest writing an extension method:
public static class ControlExtensions
{
public static void EnsureInvokeAsync(this Control control, Action action)
{
if (control.InvokeRequired) control.BeginInvoke(action);
else action();
}
}
class MyControl : UserControl
{
void M(string s)
{
// the lambda will capture the string in a closure
// the compiler does all the hard work for you
this.EnsureInvokeAsync(() => _button.Text = s);
}
}
Also, you should look into using BackgroundWorker or tasks for async operations.
If InvokeRequired is false then you don't need to worry about invoking anything at all - you're already on the right thread.
A better solution might be something like this:
public delegate void InvokerDelegate(string data);
public void DoStuff(string data){
login_submit.Text = data;
}
and then when calling it do:
if (InvokeRequired){
Invoke(InvokerDelegate(DoStuff), "something");
}
else{
DoStuff("Something");
}
A fairly common pattern you will see is to do something like this for functions that manipulate the GUI in a multithreaded environment
public delegate void InvokerDelegate();
public void DoGuiStuff(){
if (login_submit.InvokeRequired){
login_submit.Invoke(InvokerDelegate(DoGuiStuff));
return;
}
login_submit.Text = "Some value";
}
If you use the above pattern the function checks to see if an invoke is required and if so Invokes itself on the right thread. It then returns. When it invokes itself the check to see if an invoke is required returns false so it doesn't bother invoking itself again - it just runs the code.
Edit: I just went back to winforms and tried to use that pattern only to spend a couple of frustrating minutes trying to work out why I couldn't invoke a lambda. I thought I'd better come back and update this answer to add the required casting in case anyone else tried to use it.
MethodInvoker is a delegate type that doesn't have any parameters. If I understand you correctly, you can do it like this:
string txt = "some text";
MethodInvoker m = () => { login_submit.Text = txt; };
You can use closures to pass the value into the lambda's body.
string value = "Login";
MethodInvoker m = () => { login_submit.Text = value; };
if (InvokeRequired)
{
BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
Invoke(m); // I need to pass txt string in some way here.
}
or you can use class member's data

Function as an argument to a method

You can do anonymous functions in C# like you can in JavaScript:
JavaScript:
var s = (function ()
{
return "Hello World!";
}());
C#:
var s = new Func<String>(() =>
{
return "Hello World!";
})();
... In JavaScript you can pass functions to be executed by other functions. On top of that; you can pass parameters to the function which gets executed:
var f = function (message) // function to be executed
{
alert(message);
};
function execute(f) // function executing another function
{
f("Hello World!"); // executing f; passing parameter ("message")
}
Is the above example possible in C#?
Update
Use-case: I am iterating over a bunch of database, logging specific entities. Instead of calling my second function (F()) inside Log() of Logger, I'd like to call F() outside the class.
... Something along the lines of:
public void F(String databaseName)
{
}
public class Logger
{
public void Log(Function f)
{
var databaseName = "";
f(databaseName);
}
}
Absolutely - you just need to give the method an appropriate signature:
public void Execute(Action<string> action)
{
action("Hello world");
}
...
Execute(x => Console.WriteLine(x));
Note that you do have to specify the particular delegate type in the parameter - you can't just declare it as Delegate for example.
EDIT: Your database example is exactly the same as this - you want to pass in a string and not get any output, which is exactly what Action<string> does. Except if you're trying to call an existing method (F() in your code) you don't even need a lambda expression - you can use method group conversions instead:
public void F(String databaseName)
{
}
public class Logger
{
public void Log(Action<string> f)
{
var databaseName = "";
f(databaseName);
}
}
// Call it like this:
Logger logger = new Logger(...);
logger.Log(F);
You can pass delegate:
var f = (Action<string>)
(x =>
{
Console.WriteLine(x);
}
);
var execute = (Action<Action<string>>)
(cmd =>
{
cmd("Hello");
}
);
execute(f);
according your Update part:
you need a container to keep your functions
IList<Action<string>> actionList = new List<Action<Sstring>>();
in your Log() function you can add your F() to the container:
actionList.Add(F);
then invoke the function(s) somewhere outside:
foreach (Action<string> func in actionList)
{
func("databasename");
}
Like:
var s = new Func<String, string>((string name) =>
{
return string.Format("Hello {0}!", name);
});
?

Anonymous method as parameter to BeginInvoke?

Why can't you pass an anonymous method as a parameter to the BeginInvoke method? I have the following code:
private delegate void CfgMnMnuDlg(DIServer svr);
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke((CfgMnMnuDlg)ConfigureMainMenu,
new object[] { server});
}
else
{
// Do actual work here
}
}
I'm trying to avoid declaring the delegate. Why can't I write something like the below instead? Or can I, and I just can't figure out the correct syntax? The below currently generates an:
Argument type 'Anonymous method' is not assignable to parameter type 'System.Delegate'
Ok, that's right of course, but is there some other syntax I can use to do this (avoid having to declare a separate delegate in order to use BeginInvoke()?
(Being able to do this would fit in neatly with the concept of using anon methods/lamdas in place of explicit delegates which works so cleanly everywhere else.)
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke( // pass anonymous method instead ?
delegate(DIServer svr) { ConfigureMainMenu(server);},
new object[] { server});
}
else
{
// Do actual work here
}
}
Try this:
control.BeginInvoke((MethodInvoker) delegate { /* method details */ });
Or:
private void ConfigureMainMenu(DIServer server)
{
if (control.InvokeRequired)
{
control.BeginInvoke(new Action<DIServer >(ConfigureMainMenu), server);
}
else
{
/* do work */
}
}
Or:
private void ConfigureMainMenu(DIServer server)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
// Private variable
_methodInvoker = new MethodInvoker((Action)(() => ConfigureMainMenu(server)));
_methodInvoker.BeginInvoke(new AsyncCallback(ProcessEnded), null); // Call _methodInvoker.EndInvoke in ProcessEnded
}
else
{
/* do work */
}
}
You should be able to write something like this:
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke(new Action<DIServer>(ConfigureMainMenu),
new object[] { server});
}
else
{
// Do actual work here
}
}
You could write an extension method that would wrap anonymous methods, and even take care of the InvokeRequired semantics:
public static void InvokeAction(this Control ctl, Action a)
{
if (!ctl.InvokeRequired)
{
a();
}
else
{
ctl.BeginInvoke(new MethodInvoker(a));
}
}
This would allow you to do:
control.InvokeAction(delegate() { ConfigureMainMenu(server); });
You can do this in a single method by calling invoking yourself:
ClassData updData = new ClassData();
this.BeginInvoke(new Action<ClassData>(FillCurve),
new object[] { updData });
...
public void FillCurve(ClassData updData)
{
...
}
For completely anonymous methods with a limited number of parameters:
Func<int, int?> caller = new Func<int, int?>((int param1) =>
{
return null;
});
caller.BeginInvoke(7, new AsyncCallback((IAsyncResult ar) =>
{
AsyncResult result = (AsyncResult)ar;
Func<int, int?> action = (Func<int, int?>)result.AsyncDelegate;
action.EndInvoke(ar);
}), null);
You can use one of the other Func delegate types as needed.
I've tried a bunch of different methods but none work. ie...
// Fails -- cannot convert lamda to System.Delegate
mnMnu.BeginInvoke( (DIServer svr)=> {ConfigureMainMenu(server);}, new object[] server);
// Fails -- cannot convert anonymous method to System.Delegate
mnMnu.BeginInvoke( new delegate(DIServer svr){ConfigureMainMenu(server);}, new object[] server);
So, the short answer is no. You could create short helper delegates in the given context and use lambdas to make it a bit neater but that's pretty much it.
EDIT: Turns out I'm wrong. The methodinvoker answer below works.
See this page

Categories

Resources