C#.Net Delegates - c#

Say I have a method that calls another method that accepts a string and returns a string, over and over until a condition is met:
public string RetryUntil(
Func<string, string> method,
string input,
Func<string, bool> condition,
TimeSpan timeSpan)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string response = string.Empty;
bool conditionResult = false;
while (stopwatch.Elapsed < timeSpan && conditionResult != true)
{
result = method(input);
conditionResult = condition(result);
Thread.Sleep(TimeSpan.FromSeconds(0.5));
}
return response;
}
It really feels like I should be able to specify the 'method' and 'input' parameters as one parameter. So, I want to refactor it so I am able to call it like this, for example:
RetryUntil(
ConvertString("hello World"),
(str) => { return str == "whatever"; },
TimeSpan.FromSeconds(10));
But obviously, this would pass the result of calling the ConvertString method, (rather than just a delegate to that method) into the Retry method. Is there a way to pass both delegates and specific parameters for those delegates as one? Am I thinking about the entire problem backwards? It just feels a bit inelegant the way I'm doing it now.

What you're looking for is often called "currying" and is not directly supported in C#, or at least not as well as it is in F#. This is a feature where you can specify some arguments of a function, and get a delegate which takes the remaining arguments (if any) and returns the appropriate value.
The easiest way to reference this is like so:
public string RetryUntil(
Func<string> method,
Func<string, bool> condition,
TimeSpan timeSpan)
And then call via
RetryUntil(
() => ConvertString("Hello World!"),
// ...
the => creates a lambda, which will return the result of the given function. Since you're now declaring a method call, you can pass in whatever parameters you wish, or make the lambda itself take some parameters, thus currying arguments.

Related

Test a function for void return in C#

I want to write a function that takes a function as an argument and then do different things based on whether the passed-in function returns void vs a value.
C# signature checking can't tell the difference so I'm stuck doing it in code.
Is there an easy way to test whether an arbitrary function returns void?
To be clear. I explicitly am not interested in a compile error. I just want the equivalent of what I can do for any other object.
void IsString(object o) => o is string;
void ElseWhere() {
object o = 1;
if (IsString(o)) Bla();
However even this gets a compile error claiming the two methods are ambiguous. It doesn't flag the methods themselves ambiguous but I get an error on the call saying it can't resolve between them.
private static bool HasNoReturnValue(Action o) => true;
private static bool HasNoReturnValue(Func<object> o) => false;
...
if (HasNoReturnValue(SomeFunction)) Bla();
As do anything I've tried involving typeof:
if (SomeFunction is typeof(Func(object>)) Bla();
Let's say you have two methods, one of which returns a Boolean and one which returns void.
void SomeFunction1()
{
}
bool SomeFunction2()
{
return false;
}
To pass either of these as a pointer to a method, you have to convert them to a delegate. Two types of delegates: Action and Func<bool>, respectively:
var action1 = new Action(SomeFunction1);
var action2 = new Func<bool>(SomeFunction2);
You can then write two methods that accept these types as arguments:
void AcceptDelegate(Action action)
{
Console.WriteLine("The delegate returns void.");
}
void AcceptDelegate(Func<bool> func)
{
Console.WriteLine("The delegate returns a Boolean.");
}
And call them like this:
AcceptDelegate(action1);
AcceptDelegate(action2);
Or you could pass the method group directly and the compiler will figure out the type (Why? See the Microsoft documentation on c# method group conversions):
AcceptDelegate(SomeFunction1);
AcceptDelegate(SomeFunction2);
Either way you call them, you would get this output:
The delegate returns void.
The delegate returns a Boolean.
The reason this works is the compiler will automatically pick the right one at compile-time, based on the type of the delegate, just as it would pick the overload for any type such as string or integer. This is the type-safe / early-bound way to do it.
If you insist on an "any delegate"/ late binding sort of approach, you could do something like this:
void AcceptAnyDelegate(Delegate anyAction)
{
Console.WriteLine("The function returns a {0}", anyAction.Method.ReturnType);
}
Because the signature isn't type specific, you have to pass the specific delegates this time (Why? See this answer):
AcceptAnyDelegate(action1);
AcceptAnyDelegate(action2);
And the output would be:
The function returns a Void
The function returns a Boolean
Edit
After rereading your comments, I believe the confusion here is due to a misunderstanding of method groups and delegates.
When you write something like this:
Foo(Bar);
...it appears you believe you are passing to Foo a direct reference to the Bar method. That is not correct. What you are doing is specifying a method group, which the compiler can then use to infer the type of delegate to pass. If Bar is a method with no inputs or outputs, the above code is exactly the same as
Foo(new Action( Bar ));
...only the creation of the delegate is hidden from you by the compiler.
All delegates are specifically typed with respect to their parameters and return type. The Delegate base type is abstract and cannot exist in concrete form. So there is no such thing as passing a type-agnostic function reference-- it doesn't exist in c#.
If you really really want to pass something that is type-agnostic, you can ask the caller to pass a lambda expression:
Foo( () => SomeFunction1() );
You could then parse the expression to figure out the method's inputs and outputs:
void Foo(Expression<Action> anyAction)
{
var mce = anyAction.Body as MethodCallExpression;
var method = mce.Method;
Console.WriteLine("The method has a return type of {0}", method.ReturnType.Name);
}
Then to invoke the expression you would use:
var compiled = anyAction.Compile();
compiled();
That is the closest you're going to get.
There's two different types here:
Action for no return type
and
Func for a return type
Can you make two different signatures for these two different argument types?
This will do the trick
public static void TakeInAFunc<T>(T aFuncOrAction)
{
if (typeof(T) == typeof(Func<>))
{
// some value returned.
}
else if (typeof(T) == typeof(Action<>))
{
// it returns void.
}
}

Passing lambda expression as function parameter

Function like bool decide(bool x) can be passed in method as parameter as functor as:
foo(Func<bool,bool> lambda)
We can have lambda expression like ()=>{int x=8; x=x+2;} that does not take anything and return anything. Lets say I want to pass such function as parameter to another method bar then how can it be done?
This is Action, not Func. If you don't want to return value, then you must use Action.
For example:
Action<int> example1 = (int x) => Console.WriteLine("Write {0}", x);
example1.Invoke(1); // or example1(1);
Action example3 = () => Console.WriteLine("Done");
example3.Invoke(); // or example3();
You'll want an Action!
Presumably it'll do more work than set and manipulate a local variable, though?

Fit action without parameters into Action<T>

I have this method
private void ProcessItem<T>(FileSystemInfo itemInfo, int itemMinAge, Action<T> action, T parameter, string errorMessage, string successMessage = "")
which fits for 7 of my 8 calls, e.g.
ProcessItem<bool>(subDir, dir.MinAge, subDir.Delete, true, string.Format(Messages.NotDeletedFolder, subDir.FullName));
which takes a directory and then if dir.MinAge meets some criteria, is calls subDir.Delete(true) on the directory and logs a message. But I also have one call where parameter action needs to be without <T> parameter, because I need pass a void method without any parameters.
Is there a way how to fit this one call into this method? I don't want to create another method just because of one call.
ProcessItem<object>(subDir, dir.MinAge,
ignored => subDir.NoArgs(),
/* ignored */ null,
string.Format(Messages.NotDeletedFolder, subDir.FullName));
ProcessItem<bool>(subDir, dir.MinAge, _ => ParameterlessCall() , true, ...
Using _ as the parameter name in a lambda expression is a functional programming idiom for a parameter whose value never needs to be inspected.
The easiest way is probably just to use a lambda expression.
ProcessItem<bool>( ... (param) => myMethod(), ... );
In this case the parameter param will simply be ignored and myMethod will be invoked as normal.
Create an optional parameter and adjust the implementation accordingly:
private void ProcessItem<T>(FileSystemInfo itemInfo, int itemMinAge, Action<T> action = null, Action action2 = null, T parameter, string errorMessage, string successMessage = "")

How to make an anonymous method accept variable number of arguments?

I'd like to be able to make inline calls to anonymous methods with variable number of arguments (sometimes with no arguments, sometimes with 11).
Dictionary<string, Action> ActionDic = new Dictionary<string, Action>();
int i = 0;
ActionDic["something"] = () => { i += 1; }; // this line is ok
ActionDic["somethingArgs"] = (int n) => { n += 1; }; // but this is not
// Delegate 'System.Action' does not take 1 arguments
So I can't make a delegate accept arguments like that. Is my syntax wrong, or is it just not possible? Or do I have to change the type of anonymous method I should use for my dictionary?
You could use Action<int> if you want to define a delegate with 1 integer argument. For example:
Action<int> somethingArgs = (int n) => { n += 1; };
You haven't shown what the ActionDic variable is but if it is an IDictionary<string, Action> you cannot make this work because Action do not accept an argument.
What you could do on the other hand is to use a dictionary of delegates:
IDictionary<string, Delegate> ActionDic = ...
ActionDic["something"] = (Action)(() => { i += 1; });
ActionDic["somethingArgs"] = (Action<int>)((int n) => { n += 1; });
You can't. Action and Action<T> are different, incompatible delegate types.
There are several ways to compensate for this.
One would be to make ActionDic a Dictionary<string, Action<int>> but that may not satisfy all possible delegates you would want to use.
Another would be to make ActionDic something like a Dictionary<string, Delegate> but this would cumbersome to use because you need to know exactly how many many parameters (and their types) to pass each function. This information would need to be stored in another data structure somewhere.
Still a third way would be to make ActionDic a Dictionary<string, Action<object[]>> and require the delegate to unpack whatever arguments it needs from the input. The caller would be responsible for knowing exactly what arguments to use. This would allow somewhat less awkward syntax of the second option, but require more code in each delegate.
You are talking about two different delegates, i.e. Action and Action<T>. You cannot assign Action<T> to Action:
Action a1 = () => { i += 1; };
Action<int> a2 = (n) => { n += 1; };
I suppose you're having Dictionary<string,Action>, so this dictionary cannot accept a value of Action<T>.
As a side note, I'd say that the second delegate is absolutely useless, as it increments an int argument, passed by value, that is in fact doing nothing.
Also consider reading How To pass Optional Arguments. Though it has nothing to do with anonymous methods, but it may give you some clue on how to pass variable number of arguments
Action is a short way to create a delegate that returns void and take no argument.
If you want actions with argument, you need to use generic form :
Action<int> is a delegate that takes one int parameter
Action<string, double> takes 2 parameters : first is string second is double.

How to get return value of function called from a Invoke method

I need explanations.. why the following code give an: Parameter count mismatch ?
C# Code:
//...
public delegate int FindInRichTextBoxMethod(RichTextBox rtx, string target, int index);
public int FindInRichTextBox(RichTextBox rtx, string target, int index)
{
return rtx.Find(target, index, RichTextBoxFinds.None);
}
// ...
int start;
string tempState = "foo";
if (lista.InvokeRequired) {
object find = Invoke((FindInRichTextBoxMethod)delegate
{
return FindInRichTextBox(list, tempState, len);
});
start = (int)find;
} else {
start = FindInRichTextBox(list, tempState, len);
}
Thanks in advance.
The arguments to Invoke() include a delegate, and the arguments passed to that delegate. You're attempting to pass a FindInRichTextBoxMethod delegate, but that delegate type takes three arguments. You need to:
construct a delegate with your FindInRichTextBox method, and then
pass in the parameters to that delegate.
Something like this:
var finder = new FindInRichTextBoxMethod(FindInRichTextBox);
object find = Invoke(finder, new object[] { list, tempState, len });
Another route is to pass in a closure, sort of like you're attempting in your sample. In your case the error is due to the cast to a FindInRichTextBoxMethod, so the Invoke is expecting arguments. Instead, you could ignore the cast and pass in an anonymous delegate like this:
var find = Invoke(delegate { return FindInRichTextBox(list, tempState, len); });
This won't work, though, because the compiler can't determine exactly what you want to do with that anonymous delegate. Similarly, a lambda can't be automatically converted either:
var find = Invoke(() => FindInRichTextBox(list, tempState, len));
To see why and how to fix the problem, read Why must a lambda expression be cast when supplied as a plain Delegate parameter.
Are you getting this in the Invoke call? I usually pass Invoke a delegate and then an object array containing the variables you want to pass.

Categories

Resources