There are times that a method needs to be run several times until it validates. In my case there are expressions like bar.Name.Equals("John Doe") which I want to run and run until this expression validates.
Something like:
bool succeeded = TryUntillOk(bar.Name.Equals("John Doe"), 15, 100);
where TryUntillOk would be a method that runs this expression 15 times with a sleep of 100ms between each call.
I read this excellent list of answers to similar issues but in my case there is no standar delegate that this TryUntillOk method would accept.
The title of the question is not constructive. Feel free to edit it :)
You are probably looking for something like this:
bool TryRepeatedly(Func<bool> condition, int maxRepeats, TimeSpan repeatInterval)
{
for (var i = 0; i < maxRepeats; ++i)
{
if (condition()) return true;
Thread.Sleep(repeatInterval); // or your choice of waiting mechanism
}
return false;
}
Which would be invoked as follows:
bool succeeded = TryRepeatedly(() => bar.Name.Equals("John Doe"),
15,
TimeSpan.FromMilliseconds(100));
The only interesting part here is that you specify the condition as a Func<bool>, which is a delegate to a method that takes no parameters and returns a boolean. Lambda expression syntax allows you to trivially construct such a delegate at the call site.
You have to adapt the invokation. #Jon's answer has lambda invoaction, this version separates the comparand from the delegate
using System;
using System.Threading;
namespace test
{
class something
{
public String Name;
}
class Program
{
private delegate bool TryableFunction(String s);
private static bool TryUntillOk(TryableFunction f, String s, int howoften, int sleepms)
{
while (howoften-->0)
{
if (f(s)) return true;
Thread.Sleep(sleepms);
}
return false;
}
static void Main(string[] args)
{
something bar=new something();
bar.Name="Jane Doe";
bool succeeded = TryUntillOk(bar.Name.Equals,"John Doe", 15, 100);
Console.WriteLine("Succeeded with '{0}': {1}",bar.Name,succeeded);
bar.Name="John Doe";
succeeded = TryUntillOk(bar.Name.Equals,"John Doe", 15, 100);
Console.WriteLine("Succeeded with '{0}': {1}",bar.Name,succeeded);
}
}
}
You can check it
delegate void d_name(string s);
d_name obj =new d_name(bar.Name.Equals);
bool succeeded = TryUntillOk(obj("John Doe"), 15, 100);
TryUntillOk(obj d,type parameter2,type parameter3 )
{
//do your work
}
Related
How to create and use delegate like this:
public delegate void MyDel();
static void Main(string[] args)
{
MyDel myDel = delegate { return; };
Test(myDel);
}
static void Test(MyDel myDel)
{
Console.WriteLine("111");
myDel?.Invoke();
Console.WriteLine("222");
}
Output is
111
222
but I'd like to get
111.
Is it possible?
Thanks!
This is very exotic challenge. Unfortunately, I can not give you a complete answer, but I can give the direction of the search. And following article will useful to learn:
.NET CLR Injection.
It might turn out like this:
class Program
{
public delegate void MyDel();
static void Main()
{
MyDel myDel = delegate
{
// Get the method that called this delegate.
StackTrace trace = new StackTrace();
var method = trace.GetFrame(0).GetMethod();
// Get opcodes of given method.
RuntimeHelpers.PrepareMethod(method.MethodHandle);
byte[] ilCodes = method.GetMethodBody().GetILAsByteArray();
for (int i = 0; i < ilCodes.Length; i++)
{
// Find and replace opcodes to insert a return statement.
// ...
}
// Then inject opcodes back to the original method.
// ...
};
Test(myDel);
}
static void Test(MyDel myDel)
{
Console.WriteLine("111");
myDel?.Invoke();
Console.WriteLine("222");
}
}
This is just a guess, not 100% sure it will work. And I know, it's madskills for this task.
I don't know what you really trying to do here.
But in your case, with the current implementation, it is impossible to have 111 only as output. Those three statements will be executed no matter what the delegate does.
Console.WriteLine("111");
myDel?.Invoke();
Console.WriteLine("222");
However if you put the third line that outputs 222 in the delegate body surrounded with some if() { ... } statements, maybe it will print 111 only (when the conditions are not satisfied obviously).
I have a Queue that holds a list of delegates that correspond to methods that I want to run in the future. I would like to only have singular instances of any particular method/parameter in queue. In other words, a queue of DoOne(), DoTwo(2), DoThree(3) should be possible where as a queue of DoOne(), DoTwo(2), DoTwo(2) should not be allowed.
I have noticed that _queue.Contains(Func< int >) works through the minimal testing that I have done, but I am worried if I am missing something. Is this a sufficient enough test to determine whether a particular method/parameter is queued, to satisfy what I am trying to accomplish?
Queue<Func<int>> _queue = new Queue<Func<int>>();
void Queue(Func<int> Method)
{
if (!_queue.Contains(Method))
_queue.Enqueue(Method);
}
void QueueOne()
{
Queue( () => DoOne() );
}
void QueueTwo(int val)
{
Queue( () => DoTwo(val) );
}
void DoOne()
{
return 1;
}
void DoTwo(int val)
{
return val;
}
Since each time you call QueueOne or QueueTwo, you create a new function that is passed to the Queue function, I have my doubts that you can find a way to compare those to each other and determine that they match.
This leads me to recommend that you pass an identifier that you will use to make the uniqueness comparison.
In my sample code below, I chose to do this using the CallerMemberName to identify the name of the calling function (i.e. "QueueOne" or "QueueTwo") and refuse to enqueue the item if the queue still had a matching entry.
Queue<Tuple<string, Func<int>>> _queue = new Queue<Tuple<string, Func<int>>>();
void Queue(Func<int> method, [CallerMemberName] string caller = null)
{
if (!_queue.Any(v => v.Item1 == caller))
_queue.Enqueue(Tuple.Create(caller, method));
}
void QueueOne()
{
Queue(() => DoOne());
}
void QueueTwo(int val)
{
Queue(() => DoTwo(val));
}
int DoOne()
{
return 1;
}
int DoTwo(int val)
{
return val;
}
Since you are calling with a parameter they are treated different objects (see Closures in c#)
Change your logicto check the duplication to:
if (!_queue.Where(x => x.Method == Method.Method).Any())
_queue.Enqueue(m);
this will help you to stop adding same method again (even if they have diff parameters)
I want my code to keep trying a method until no exception is thrown, however, unlike this question, I would like it to be written as a generic method capable of running any input delegate/method. Here is what I've had in mind, but I am not sure how to pass the arguments or generic methods through it:
public void tryLoop(Delegate anyMethod, T[] arguments) {
while (true) {
// Could be replaced by timer timeout
try {
anyMethod(arguments);
break;
}
catch {
System.Threading.Thread.Sleep(2000); // wait 2 seconds
}
}
}
Is this possible?
EDIT: For the academics of it, I would also be curious to know if it's possible to return the result as well.
If you can settle for using closures, you won't need to have to pass parameters to this method all (or have multiple overloads). The result can be returned using a Func.
public T tryLoop<T>(Func<T> anyMethod)
{
while (true)
{
try
{
return anyMethod();
}
catch
{
System.Threading.Thread.Sleep(2000); // *
}
}
return default(T);
}
void SomeMethod(int param)
{
var someLocal = "Hi";
var anotherLocal = 0;
var result = tryLoop(() =>
{
Console.WriteLine(someLocal);
return param + anotherLocal;
});
Console.Write(result);
}
To be honest, I wouldn't set an infinite retry, however - at best, if certain types of retryable error were returned, such as a database deadlock, or a timeout calling an erratic web service, then possibly 3 or 4 retries might be in order. Exceptions such DivideByZero or FileNotFound aren't likely to go away by running them indefinitely :-)
*** especially with deadlocks, sleeping for a random period is recommended, just in case other threads are also simultaneously deadlocking on exactly the same data - you don't want the same deadlock recurrence happening in 2000ms :-).
A way to do this is by using an Action, and remove the arguments parameter:
public void tryLoop(Action anyMethod) {
while ( true ) {
// Could be replaced by timer timeout
try {
anyMethod();
break;
}
catch {
System.Threading.Thread.Sleep(2000); // wait 2 seconds
}
}
}
This gives you ultimate freedom in how to use it:
tryLoop(() => string.Reverse("abc"));
or like this:
String s1 = "A";
String s2 = "b";
tryLoop(() => string.Concat(s1, s2));
As you can see in the second example, you can directly take the arguments from the context of the tryLoop method being called. You can invoke anything there.
The good thing for this approach is that you will not have to use Invoke or DynamicInvoke as with a Delegate instead of Action, because these introduce a performance penalty.
If you need the result, you can re-write the above with a Func<T> instead of Action, like this:
public T tryLoop<T>(Func<T> anyMethod) {
while ( true ) {
// Could be replaced by timer timeout
try {
return anyMethod();
}
catch {
System.Threading.Thread.Sleep(2000); // wait 2 seconds
}
}
}
and use it like this:
var reversed = tryLoop(() => string.Reverse("abc"));
String s1 = "A";
String s2 = "b";
var concatenated = tryLoop(() => string.Concat(s1, s2));
Check if this suit your needs. If not then please comment. Also not sure how it will be performance wise.
Also the DynamicInvoke method has an object return type. This you can use to return the result of the delegate. You can change the return type of method from void to object.
public void tryLoop<T>(Delegate anyMethod, T[] arguments)
{
while (true)
{ // Could be replaced by timer timeout
try
{
anyMethod.DynamicInvoke(arguments);
break;
}
catch
{
System.Threading.Thread.Sleep(2000); // wait 2 seconds
}
}
}
Hope this helps
I can't explain an issue I've run across. Basically I get a different answer if I use lambda syntax in a foreach loop than if I use it in a for loop. In the code below I register a delegate in a "dispatcher" class. I then later wrap the delegate on the way out in another delegate and return a list of these wrapped delegates. I then execute them. The expected output of executing the wrapped function list is 1,2. However I don't see that when I combine a lambda and a foreach loop.
This is not the code that is causing the problem, but the simplest case I could make to reproduce it. I would prefer not to discuss use cases of this, I'm more curious as to why I get behavior I'm not expecting. If I use the foreach loop below with the lambda syntax it fails. If I use the new Action() syntax and a foreach it works, if I use the lambda syntax in a for loop it works. Can anyone explain what is going on here. This has me really stumped.
public class Holder
{
public Holder(int ID, Dispatcher disp)
{
this.ID = ID;
disp.Register(Something);
}
public int ID { get; set; }
private void Something(int test) { Console.WriteLine(ID.ToString()); }
}
public class Dispatcher
{
List<Action<int>> m_Holder = new List<Action<int>>();
public void Register(Action<int> func)
{
m_Holder.Add(func);
}
public List<Action<int>> ReturnWrappedList()
{
List<Action<int>> temp = new List<Action<int>>();
//for (int i = 0; i < m_Holder.Count; i++) //Works - gives 1, 2
//{
// var action = m_Holder[i];
// temp.Add(p => action(p));
//}
foreach (var action in m_Holder)
{
temp.Add(p => action(p)); //Fails - gives 2,2
//temp.Add(new Action<int>(action)); Works - gives 1,2
}
return temp;
}
}
class Program
{
static void Main(string[] args)
{
var disp = new Dispatcher();
var hold1 = new Holder(1, disp);
var hold2 = new Holder(2, disp);
disp.ReturnWrappedList().ForEach(p => p(1));
}
}
This is the infamous "closing over the loop variable" gotcha.
Closing over the loop variable considered harmful (and part two)
Have you tried:
foreach (var action in m_Holder)
{
var a = action;
temp.Add(p => a(p));
}
This is the classic issue of a captured closure with a scope that isn't what you expect. In the foreach, the action has outer scope, so the execution captures the last value of the loop. In the for case, you create the action in inner scope, so the closure is over the local value at each iteration.
I'm trying to do this, but it doesn't work. Some suggestions?
int test_i = 0;
DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(test_i);
test_i <- still is 0 and not 3!!!
public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(int i)
{
DisableUi();
m_commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
ConnectToServer();
i = 3; <--------------------------
// This is the continuation that will be run
// on the UI thread
return () =>
{
EnableUi();
};
});
}
Why I can't set test_i to 3? I also tried ref and out, but it doesn't work.
What can I do to fix it?
EDIT
I've tried this, but ouside of this method dataSet still is empty.
public static void Select(DataGridView dataGridView, ref DataSet dataSet, params object[] parameters)
{
var _dataSet = dataSet;
AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
_dataSet = getDataFromDb(parameters);
// This is the continuation that will be run on the UI thread
return () =>
{
dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
};
});
dataSet = _dataSet;
}
When passing the variable using the ref keyword, you can't use it inside a lambda expression. Try using a local variable inside the lambda and assign the ref variable outside it, if possible (somewhat simplified example):
private static void Main(string[] args)
{
int i = 0;
DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref i);
Console.WriteLine(i);
}
public static void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref int i)
{
int temp = i;
Thread t = new Thread(() =>
{
temp = 3; // assign the captured, local variable
});
t.Start();
t.Join();
i = temp; // assign the ref parameter
}
Update
In response to the updated answer: your problem is that _dataSet inside the lambda expression is not the same variable as dataSet outside the lambda expression. What you could do is the following:
class DataSetContainer
{
public DataSet DataSet { get; set; }
}
Now we have a reference type with a property that we can safely modify inside the lambda expression:
public static void Select(DataGridView dataGridView,
DataSetContainer dataSetContainer,
params object[] parameters)
{
AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
dataSetContainer.DataSet = getDataFromDb(parameters);
// This is the continuation that will be run on the UI thread
return () =>
{
dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
};
});
}
}
In the above code, the lambda expression will update the DataSet property of the DataSetContainer instance that is passed to the Select method. Since you are not modifying the passed argument itself but only a member of that instance there is no need for the ref keyword, and we also get around the closure issue.
Update 2
And now when I switched on my brain, I realize that the Select method makes an asynchronous call. It is quite likely as the code looks that the last line is the Select method will be executed long before _dataSet is being assigned, and as a result it will be null. To get around this you probably want to look into using some sort of signaling mechanism (such as ManualResetEvent or AutoResetEvent) to know when the assignment is done.
The i variable in the lambda expression refers to the parameter i of the method. As a workaround, you can make it to refer to a global variable (dirty solution).
By the way, you can't capture ref and out variables in lambdas, but you can have them as parameters. You need to change the signature of your delegate and the implementation of the method receiving the delegate, which might not be suitable:
(out int i) => { i = 10; }