Consider the following pseudo code:
TResult Foo<TResult>(Func<T1, T2,...,Tn, TResult> f, params object[] args)
{
TResult result = f(args);
return result;
}
The function accepts Func<> with unknown number of generic parameters and a list of the corresponding arguments. Is it possible to write it in C#? How to define and call Foo? How do I pass args to f?
You can use Delegate with DynamicInvoke.
With that, you don't need to handle with object[] in f.
TResult Foo<TResult>(Delegate f, params object[] args)
{
var result = f.DynamicInvoke(args);
return (TResult)Convert.ChangeType(result, typeof(TResult));
}
Usage:
Func<string, int, bool, bool> f = (name, age, active) =>
{
if (name == "Jon" && age == 40 && active)
{
return true;
}
return false;
};
Foo<bool>(f,"Jon", 40, true);
I created a fiddle showing some examples: https://dotnetfiddle.net/LdmOqo
Note:
If you want to use a method group, you need to use an explict casting to Func:
public static bool Method(string name, int age)
{
...
}
var method = (Func<string, int, bool>)Method;
Foo<bool>(method, "Jon", 40);
Fiddle: https://dotnetfiddle.net/3ZPLsY
That's not possible. At best, you could have a delegate that also takes a variable number of arguments, and then have the delegate parse the arguments
TResult Foo<TResult>(Func<object[], TResult> f, params object[] args)
{
TResult result = f(args);
return result;
}
Foo<int>(args =>
{
var name = args[0] as string;
var age = (int) args[1];
//...
return age;
}, arg1, arg2, arg3);
This could become easy with lambda expressions:
TResult Foo<TResult>(Func<TResult> f)
{
return f();
}
Then usage could be like:
var result = Foo<int>(() => method(arg1, arg2, arg3));
Where method can be arbitrary method returning int.
This way you can pass any number of any erguments directly through lambda.
To support asynchoronous code we can define:
Task<TResult> Foo<TResult>(Func<Task<TResult>> f)
{
return f();
}
// or with cancellation token
Task<TResult> Foo<TResult>(Func<CancellationToken, Task<TResult>> f, CancellationToken cancellationToken)
{
return f(cancellationToken);
}
and use it like:
var asyncResult = await Foo(async () => await asyncMethod(arg1, arg2, arg3));
// With cancellation token
var asyncResult = await Foo(
async (ct) => await asyncMethod(arg1, arg2, arg3, ct),
cancellationToken);
You could try something similar to what I posted here: https://stackoverflow.com/a/47556051/4681344
It will allow for any number of arguments, and enforces their types.
public delegate T ParamsAction<T>(params object[] args);
TResult Foo<TResult>(ParamsAction<TResult> f)
{
TResult result = f();
return result;
}
to call it, simply......
Foo(args => MethodToCallback("Bar", 123));
In some cases you may be able to get away with a trick like this:
public static class MyClass
{
private static T CommonWorkMethod<T>(Func<T> wishMultipleArgsFunc)
{
// ... do common preparation
T returnValue = wishMultipleArgsFunc();
// ... do common cleanup
return returnValue;
}
public static int DoCommonWorkNoParams() => CommonWorkMethod<int>(ProduceIntWithNoParams);
public static long DoCommonWorkWithLong(long p1) => CommonWorkMethod<long>(() => ProcessOneLong(p1));
public static string DoCommonWorkWith2Params(int p1, long p2) => CommonWorkMethod<string>(() => ConvertToCollatedString(p1, p2));
private static int ProduceIntWithNoParams() { return 5; }
}
Although it is not really what asked, a simple workaround would be to define several Foo method with different number of type arguments.
It is uncommon to have function with more than 6 parameters, so one could define the following method and get away with almost every use case, while staying type safe. Renan's solution could then be used for the remaining cases.
public TResult Foo<TResult> (Func<TResult> f)
{
return f();
}
public TResult Foo<T1, TResult>(Func<T1, TResult> f, T1 t1)
{
return f(t1);
}
public TResult Foo<T1, T2, TResult>(Func<T1, T2, TResult> f, T1 t1, T2 t2)
{
return f(t1, t2);
}
...
Related
First, foo is a Func<T1,T2,TResult> object.
Is is possible do something like
Func<T2,T1,TResult> bar = ConvertFunction(foo);
thus convert Func<T1,T2,TResult> to Func<T2,T1,TResult>.
Yes, that's possible:
Func<T2, T1, TResult> bar = (t2, t1) => foo(t1, t2);
That basically creates another delegate with switched parameters that internally simply calls the original delegate.
This is the only way to perform this kind of "conversion" if you only have a Func<T1, T2, TResult> and not a Expression<Func<T1, T2, TResult>>.
Here's the function:
class MyFuncConverter<T1, T2, TResult>
{
static Func<T1, T2, TResult> _foo;
public static Func<T2, T1, TResult> ConvertFunction(Func<T1, T2, TResult> foo)
{
_foo = foo;
return new Func<T2, T1, TResult>(MyFunc);
}
private static TResult MyFunc(T2 arg2, T1 arg1)
{
return _foo(arg1, arg2);
}
}
Sample usage:
static void Main(string[] args)
{
var arg1 = 10;
var arg2 = "abc";
// create a Func with parameters in reversed order
Func<string, int, string> testStringInt =
MyFuncConverter<int, string, string>.ConvertFunction(TestIntString);
var result1 = TestIntString(arg1, arg2);
var result2 = testStringInt(arg2, arg1);
// testing results
Console.WriteLine(result1 == result2);
}
/// <summary>
/// Sample method
/// </summary>
private static string TestIntString(int arg1, string arg2)
{
byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII
.GetBytes(arg2.ToString() + arg1);
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
I have several methods all returning void with different signature (parameters) and different names. I need to pass those methods as a parameter in a generic method that will invoke it latter. Also that need to be as transparent as possible.
Following what I got so far:
private void button1_Click(object sender, EventArgs e)
{
Action<string,int> TestHandler = Test;
InvokeMyMethod(TestHandler);
Action<string, int,object > TestHandlerN = TestN;
InvokeMyMethod(TestHandlerN);
}
public void InvokeMyMethod(Delegate Method)
{
object[] args = new object[X];
Method.DynamicInvoke();
}
public void Test(string t1, int t2)
{
MessageBox.Show(t1 + t2);
}
public void TestN(string t1, int t2, object t3)
{
MessageBox.Show(t1 + t2);
}
That is what I need:
private void button1_Click(object sender, EventArgs e)
{
InvokeMyMethod(Test);
InvokeMyMethod(TestN);
}
public void InvokeMyMethod(XXX_Type Method)
{
object[] args = new object[X];
Method.DynamicInvoke(args);
}
public void Test(string t1, int t2)
{
MessageBox.Show(t1 + t2);
}
public void TestN(string t1, int t2, object t3)
{
MessageBox.Show(t1 + t2);
}
This doesn't answer your question directly, but here is how I solve a similar problem:
public static partial class Lambda
{
public static Action Pin<T0>
(
this Action<T0> action,
T0 arg0
)
{
return () => action(arg0);
}
public static Func<TResult> Pin<T0, TResult>
(
this Func<T0, TResult> func,
T0 arg0
)
{
return () => func(arg0);
}
public static Action Pin<T0, T1>
(
this Action<T0, T1> action,
T0 arg0,
T1 arg1
)
{
return () => action(arg0, arg1);
}
public static Func<TResult> Pin<T0, T1, TResult>
(
this Func<T0, T1, TResult> func,
T0 arg0,
T1 arg1
)
{
return () => func(arg0, arg1);
}
// More signatures omitted for brevity...
// I would love it if C# supported variadic template parameters :-)
}
The idea is that if you have an Action which requires arguments, you can say:
Action<int, string> foo;
Action a = foo.Pin(5, "bleh");
Have the code generator.
Likewise, you might want to have a way to curry to some other delegate type (like Action<string, int>). The difference between my method and yours is that mine is not late-bound, but you do not appear to be using late-binding anyway, so early-binding gives you some compile-time type safety.
I'm not sure you can do what I think you are asking. You call method.DynamicInvoke with some object[], but how did you get values for those parameters?
And to answer the question before anybody asks it, I created the Lambda.Pin functions because I was tired of making this mistake:
Action<int> foo;
foreach (int i in someList)
AddAction(() => foo(i));
public void InvokeMyMethod(Delegate method) {
Method.DynamicInvoke(new object[] {"Test", 1});
}
But you need to invoke it like
InvokeMyMethod((Action<string, int>)Test);
I'm trying to remove some duplication from this code, and have it easily support functions with more parameters.
How would you improve this code and allow for more complex functions?
Also, I'm worried about my key generation, some objects won't serialize to a string distinctly, and just return their typename, not a unique value. Suggestions?
Edit: I've used ChaosPandion's answer, and got it down to this
using System;
using System.Web.Caching;
public static class Memoize
{
public static Cache LocalCache = System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache;
public static TResult ResultOf<TArg1, TResult>(Func<TArg1, TResult> func, long durationInSeconds, TArg1 arg1)
{
var key = HashArguments(func.Method.Name, arg1);
return ResultOf(key, durationInSeconds, () => func(arg1));
}
public static TResult ResultOf<TArg1, TArg2, TResult>(Func<TArg1, TArg2, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2)
{
var key = HashArguments(func.Method.Name, arg1, arg2);
return ResultOf(key, durationInSeconds, () => func(arg1, arg2));
}
public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(Func<TArg1, TArg2, TArg3, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3)
{
var key = HashArguments(func.Method.Name, arg1, arg2, arg3);
return ResultOf(key, durationInSeconds, () => func(arg1, arg2, arg3));
}
public static TResult ResultOf<TArg1, TArg2, TArg3, TArg4, TResult>(Func<TArg1, TArg2, TArg3, TArg4, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
{
var key = HashArguments(func.Method.Name, arg1, arg2, arg3, arg4);
return ResultOf(key, durationInSeconds, () => func(arg1, arg2, arg3, arg4));
}
private static TResult ResultOf<TResult>(string key, long durationInSeconds, Func<TResult> func)
{
return LocalCache.Get(key) != null
? (TResult)LocalCache.Get(key)
: CacheResult(key, durationInSeconds, func());
}
public static void Reset()
{
var enumerator = LocalCache.GetEnumerator();
while (enumerator.MoveNext())
LocalCache.Remove(enumerator.Key.ToString());
}
private static T CacheResult<T>(string key, long durationInSeconds, T value)
{
LocalCache.Insert(key, value, null, DateTime.Now.AddSeconds(durationInSeconds), new TimeSpan());
return value;
}
private static string HashArguments(params object[] args)
{
if (args == null)
return "noargs";
var result = 23;
for (var i = 0; i < args.Length; i++)
{
var arg = args[i];
if (arg == null)
{
result *= (31 * i + 1);
continue;
}
result *= (31 * arg.GetHashCode());
}
return result.ToString();
}
}
Here is my version which removes quite a bit of duplication and should produce a much more reliable range of keys.
EDIT
My latest version produces a pretty reliable distribution. Take a look at the example I placed in the Main method.
class Program
{
public static class Memoize
{
public static Cache LocalCache =
System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache;
public static TResult ResultOf<TArg1, TResult>(
Func<TArg1, TResult> func,
TArg1 arg1,
long durationInSeconds)
{
var key = HashArguments(
func.Method.DeclaringType.GUID,
typeof(TArg1).GUID,
(object)arg1);
return Complete(key, durationInSeconds, () => func(arg1));
}
public static TResult ResultOf<TArg1, TArg2, TResult>(
Func<TArg1, TArg2, TResult> func,
TArg1 arg1,
TArg2 arg2,
long durationInSeconds)
{
var key = HashArguments(
func.Method.DeclaringType.GUID,
typeof(TArg1).GUID,
(object)arg1,
typeof(TArg2).GUID,
(object)arg2);
return Complete(key, durationInSeconds, () => func(arg1, arg2));
}
public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(
Func<TArg1, TArg2, TArg3, TResult> func,
TArg1 arg1,
TArg2 arg2,
TArg3 arg3,
long durationInSeconds)
{
var key = HashArguments(
func.Method.DeclaringType.GUID,
typeof(TArg1).GUID,
(object)arg1,
typeof(TArg2).GUID,
(object)arg2,
typeof(TArg3).GUID,
(object)arg3);
return Complete(key, durationInSeconds, () => func(arg1, arg2, arg3));
}
public static void Reset()
{
var enumerator = LocalCache.GetEnumerator();
while (enumerator.MoveNext())
LocalCache.Remove(enumerator.Key.ToString());
}
private static T CacheResult<T>(string key, long durationInSeconds, T value)
{
LocalCache.Insert(
key,
value,
null,
DateTime.Now.AddSeconds(durationInSeconds),
new TimeSpan());
return value;
}
static T Complete<T>(string key, long durationInSeconds, Func<T> valueFunc)
{
return LocalCache.Get(key) != null
? (T)LocalCache.Get(key)
: CacheResult(key, durationInSeconds, valueFunc());
}
static string HashArguments(params object[] args)
{
if (args == null)
return "null args";
int result = 23;
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
if (arg == null)
{
result = 31 * result + (i + 1);
continue;
}
result = 31 * result + arg.GetHashCode();
}
return result.ToString();
}
}
static int test(int a, int b)
{
return a + b;
}
private static class Inner
{
public static int test(int a, int b)
{
return a + b;
}
}
static int test(int a, object b)
{
return a + (int)b;
}
static void Main(string[] args)
{
Memoize.ResultOf<int, int, int>(test, 1, 2, 100000);
Memoize.ResultOf<int, int, int>(test, 2, 1, 100000);
Memoize.ResultOf<int, int, int>(Inner.test, 1, 2, 100000);
Memoize.ResultOf<int, int, int>(Inner.test, 2, 1, 100000);
Memoize.ResultOf<int, object, int>(test, 1, 2, 100000);
Memoize.ResultOf<int, object, int>(test, 2, 1, 100000);
}
}
The basic idea in ChaosPandion's code is correct, but there are some fatal flaws that must be fixed in order for this to work reliably.
1) Rather than using a hash of hashes, you need to make a genuinely unique key. One way would be to concatenate the string representations of each element in the parameter array, with a delimiter. This way, once a result has been memoized, it will consistently be retrieved, without false positives.
Chaos' trick with the GUID of the method is useful here, but this avoids the problem of depending on GetHashCode being truly unique as well as counting on a fairly simple hash of hashes to remain unique. Instead, the full string will get hashed, but there will be a full character-by-character comparison to rule out mismatches.
Admittedly, concatenating a big string is not particularly elegant and might even impact performance. Moreover, this still leaves you with the issue of classes whose ToString does not give instance-specific information. However, there are solutions for this based on reflection.
2) A very simple optimization would be to call LocalCache.Get just once in Complete. This is a potentially expensive operation, so there's really no excuse for doubling the cost.
Other than that, go with what ChaosPandion suggested.
I have to call a method whose name is coming from a configuration file. I can achieve this using Reflection.MethodInfo.Invoke() method. But my scenario is all these methods should be of same signature. Can i implement it using Delegates? but how can i add a method name stored in configuration file to a delegate?
Look at Delegate.CreateDelegate on MSDN. Some of the best docs there!
You can create a re-usable delegate if you wanted to, e.g. given my type:
public class MyClass
{
public void DoSomething(string argument1, int argument2)
{
Console.WriteLine(argument1);
Console.WriteLine(argument2);
}
}
I could do something like:
Action<object, MethodInfo, string, int> action =
(obj, m, arg1, arg2) => m.Invoke(obj, new object[] { arg1, arg2 });
And call it as:
var method = typeof(MyClass).GetMethod("DoSomething");
var instance = new MyClass();
action(instance, method, "Hello", 24);
If you know your method has a return type, you can do that with a System.Func delegate:
public class MyClass
{
public string DoSomething(string argument1, int argument2)
{
return string.Format("{0} {1}", argument1, argument2);
}
}
Func<object, MethodInfo, string, int, string> func =
(obj, m, arg1, arg2) => (string)m.Invoke(obj, new object[] { arg1, arg2 });
string result = func(instance, method, "Hello", 24);
I am trying to get my head around the use of the Action delegate type for use in forcing a timeout when methods called in a 3rd party COM dll hang up. After much searching I find that I can use Action<> or Func<> and pass up to 4 generic parameters depending on whether the method called returns a parameter or not.
For this instance I wish to call a timeout on a series of methods that return void and take 2 parameters. What follows is the code that I am putting together, but I am unable to determine how to correctly code the BeginInvoke, I am prompted to place "T arg1" and "T arg2" but when I enter param1 or param2 VS2008 tells me that these values are indeterminate.
Here is the code as it is so far:
static void CallAndWait(Action<T, T> action, int timeout)
{
Thread subThread = null;
Action<T, T> wrappedAction = (param1, param2) =>
{
subThread = Thread.CurrentThread;
action(param1, param2);
};
IAsyncResult result = wrappedAction.BeginInvoke(param1, param2, null, null);
if (((timeout != -1) && !result.IsCompleted) &&
(!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
{
if (subThread != null)
{
subThread.Abort();
}
//TODO: close external resource.
throw new TimeoutException();
}
else
{
action.EndInvoke(result);
}
}
Any ideas on what is wrong here would be much appreciated.
Below is a re-edited code based on the first comment
Thanks for the input so far. The following compiles. I just can't seem to get the syntax right in calling it.
public static void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)
{
Thread subThread = null;
T1 param1 = default(T1);
T2 param2 = default(T2);
Action<T1, T2> wrappedAction = (p1, p2) =>
{
subThread = Thread.CurrentThread;
action(param1, param2);
};
IAsyncResult result = wrappedAction.BeginInvoke(param1, param2, null, null);
if (((timeout != -1) && !result.IsCompleted) &&
(!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
{
if (subThread != null)
{
subThread.Abort();
}
//TODO: close external resource.
throw new TimeoutException();
}
else
{
action.EndInvoke(result);
}
}
I am trying to test this by calling the following method with it:
public void LongTimeProcess(int a, string b)
{
Thread.Sleep(a);
}
But the following code is not correct:
Action<int, string> action = (s1, s2) => LongTimeProcess(s1, s2);
CallAndWait<int, string>(action(1500, "hello"), 500);
Updated code
I've posted the code for future reference by forum users. The code below appears to work.
The only point to check is that my unit test causes an exception to be thrown when calling the routine a second time on the same function at the point where we "action.EndInvoke(result)" as the result is not associated with the action. This is probably because my LongProcess is just a Thread.sleep, which in this instance will mean that it hasn't aborted by the time that my second call is made.
public static void CallAndWait<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2, int timeout)
{
Thread subThread = null;
Action<T1, T2> wrappedAction = (p1, p2) =>
{
subThread = Thread.CurrentThread;
action(arg1, arg2);
};
IAsyncResult result = wrappedAction.BeginInvoke(arg1, arg2, null, null);
if (((timeout != -1) && !result.IsCompleted) &&
(!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
{
if (subThread != null)
{
subThread.Abort();
}
//TODO: close external resource.
throw new TimeoutException();
}
else
{
action.EndInvoke(result);
}
}
At first it should probably be
static void CallAndWait<T>(Action<T, T> action, int timeout)
instead of
static void CallAndWait(Action<T, T> action, int timeout)
and if the parameters have different types even the following.
static void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)
But I don't think that is all. Going to look at it again.
UPDATE
Now I can see your problem ... you are calling the action when you try to call CallAndWait(). The call must be the following
CallWithTimeout.CallAndWait(action, 1500, "hello", 500);
instead of your call.
CallWithTimeout.CallAndWait<int, string>(action(1500, "hello"), 500);
So you have to change the method signature from
void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)
to
void CallAndWait<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2, int timeout)
modify the body a bit and you should be done.