I have the following F# function
let Fetch logger id =
logger "string1" "string2"
// search a database with the id and return a result
In my C# class I want to call the Fetch F# function mocking out the logger function.
So I have the following C# function as the mocker.
void Print(string x, string y) { // do nothing }
I'm trying to call the F# function from a C# class with the following.
var _logger = FuncConvert.ToFSharpFunc<string, string>(Print);
var _result = Fetch(logger, 3);
The problem with FuncConvert.ToFSharpFunc is that takes only one type argument.
When I change the Fetch F# logger function to the following it works fine when I use ToFSharpFunc(Print) where the C# Print function also takes in one string.
let Fetch logger id =
logger "string1"
// search a database with the id and return a result
Anyone got ideas?
Short Version
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=>Print(t.Item1,t.Item2));
var logger = FuncConvert.FuncFromTupled(tupleLogger);
MyOtheProject.MyModule.Fetch(logger, 3);
Long Version
F# functions only accept one argument. Multiple arguments essentially create nested functions. You need to do the same on C#'s side.
Check the type of the logger parameter in C# with Intellisense. It's
Microsoft.FSharp.Core.FSharpFunc<string, Microsoft.FSharp.Core.FSharpFunc<string, a>>
FuncConvert.ToFSharpFunc can't create this. FuncFromTupled though can create this from an FSharpFunc that takes a Tuple with multiple fields as an argument.
That's something that can be created by ToFsharpFunc :
FSharpFunc<Tuple<string,string>,Unit> tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(
t=> Print(t.Item1,t.Item2));
or
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=> Print(t.Item1,t.Item2));
As FuncFromTupled's description says, it's A utility function to convert function values from tupled to curried form.. tupleLogger is a tupled form that we need to convert to a curried form:
var logger = FuncConvert.FuncFromTupled(tupleLogger);
The resulting code looks like:
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=>Print(t.Item1,t.Item2));
var logger = FuncConvert.FuncFromTupled(tupleLogger);
MyOtheProject.MyModule.Fetch(logger, 3);
You could create a utility function to combine the two conversions :
public static class MyFuncConvert
{
public static FSharpFunc<T1, FSharpFunc<T2, Unit>> ToFSharpFunc<T1, T2>(Action<T1, T2> action)
{
var tupled = FuncConvert.ToFSharpFunc<Tuple<T1, T2>>(
t => action(t.Item1, t.Item2));
var curried = FuncConvert.FuncFromTupled(tupled);
return curried;
}
}
Which would allow you to write :
var logger = MyFuncConvert.ToFSharpFunc<string,string>(Print);
F# functions can have only one argument. When you have for example: logger "string1" "string2" the expression logger "string1" creates another function with "string1" inside it which then can take "string2" as an argument. So to convert this you can create such helper method:
public static class FuncConvertExt
{
public static FSharpFunc<T1, FSharpFunc<T2, Unit>> Create<T1, T2>(Action<T1, T2> action)
{
Converter<T1, FSharpFunc<T2, Unit>> conv = value1 =>
{
return FuncConvert.ToFSharpFunc<T2>(value2 => action(value1, value2));
};
return FSharpFunc<T1, FSharpFunc<T2, Unit>>.FromConverter(conv);
}
}
Then you can do this:
var func = FuncConvertExt.Create<string, string>(Print);
func.Invoke("aaa").Invoke("bbb");
More info: https://blogs.msdn.microsoft.com/jaredpar/2010/07/27/converting-system-funct1-tn-to-fsharpfuncttresult/
Related
I have a method which takes 2 parameters. I want to build a method in runtime, which will call this method and pass one parameter by default. Another parameter will be passed in new function.
I tried to create lambda expression which calls this method but i've got an error: Incorrect number of arguments supplied for call to method.
static class Program
{
static void Main(string[] args)
{
var method = typeof(Program).GetMethod("Method");
// here i want to set first parameter as "parameter1" when new method will be called
var lambda = Expression.Lambda<Func<string, string>>(Expression.Call(method, Expression.Constant("parameter1")));
var adapter = lambda.Compile();
// and here i wanna pass only one agrument - second (parameter2)
var result = adapter("parameter2");
// expected result "parameter1 ---- parameter2"
}
public static string Method(string parameter1, string parameter2)
{
return $"{parameter1} ---- {parameter2}";
}
I wanna pass only second parameter when function will be called. First must be specified automatically.
You defined the constant, but you also need to define the other parameter so you can give it to Method:
var method = typeof(Program).GetMethod("Method");
// here i want to set first parameter as "parameter1" when new method will be called
var param = Expression.Parameter(typeof(string));
var call = Expression.Call(method, Expression.Constant("parameter1"), param);
var lambda = Expression.Lambda<Func<string, string>>(call, param);
var adapter = lambda.Compile();
// and here i wanna pass only one agrument - second (parameter2)
var result = adapter("parameter2");
Of course I'm going to assume you have a real use-case for doing that dynamically. Otherwise you could just write:
Func<string, string> adapter = p => Method("parameter1", p);
I have list of public void function that get one parameter to execute and I want to use loop to do it,
I do not know how to do this, can you advise me?
I thought to insert the names of mt functions to arr and than run in loop
something like this
string[] s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.lengh;i++)
here I want to call the function ... how can I do it?
Do you have better offer?
Thanks.
You can pass functions as parameters / arguments by the use of delegates:
Action<T> action1 = func1;
Action<T> action2 = func2;
where T is the type of the parameter (e.g. int, string)
You can then run these referenced functions by calling
action1(t);
action2(t);
where t is the parameter for your function.
To make this example useful, consider creating a list of actions:
List<Action<T>> actions = new List<Action<T>>();
actions.Add(action1); actions.Add(action2);
foreach (Action<T> action in actions)
{
var t = param; // Replace param with the input parameter
action(t);
}
Of course, you must also have
using System;
at the top of your code file to reference Action.
See also the MSDN documentation on the Action delegate: http://msdn.microsoft.com/en-us/library/018hxwa8.aspx
Your first option is to use delegates (assuming the argument is an integer):
var s1 = new Action<int>[3] { a => func1(a), a => func2(a), a => func3(a) }; // without quotes it creates a function pointer
for(int i=0;i<s1.Length;i++)
s1[i](parameter); // call the delegate
If you do not know the function names at compile time, use reflection to call the method:
var s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.Length;i++)
this.GetType().GetMethod(s1[i]).Invoke(this, new object[] { parameter });
Note the this.GetType() in the second sample - if the methods are defined on another type you will most probably use typeof(OtherType) instead.
Use a delegate. For example, to invoke a method that takes one parameter and returns no value (i.e. returns void), use the Action<T> delegate. Assuming that you want them all to accept the same parameter type, it would look a bit like this:
public void Action1(int x) { ... }
public void Action2(int x) { ... }
public void Action3(int x) { ... }
...
Action<int>[] actions = new Action<int>[] { Action1, Action2, Action3 }
for (int i = 0; i < actions.Length; i++)
{
actions[i](i); // Invoke the delegate with (...)
}
Further Reading
Delegates (C# Programming Guide)
I believe what you are wanting to do could be accomplished via a collection of actions.
Assuming the type of parameter for each function is integer, here's how that could look:
List<Action<int>> functions = new List<Action<int>> {func1, func2, func3};
int i = 5;
foreach (Action<int> f in functions)
{
f(i);
}
EDIT: updated per updated OP that specifies the looping should only be over each of the functions.
var list = new List<Action<MyParameterType>>() {func1, func2, func3};
foreach(var func in list)
{
func(someValue);
}
string[] s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.lengh;i++)
List<string, Func<string>> functionList = new List<string, Func<string>>();
functionList.Add(s1[0], ()=>{return "You called func1!";});
functionList.Add(s1[1], ()=>{return "You called func2!";});
functionList.Add(s1[2], ()=>{return "You called func3!";});
for(int i=0;i<s1.length;i++)
{
string retVal = functionList[s1[i]].Invoke();
}
i have 5 datafunctions which all return the same type of object (List<source>)
Now i have to publish them in a WCF in which i have to surround the called code with all kind of error handling code (about 50 lines).
So i thought: because the code (51 lines) is all the same except the one line to get the data, just create one function with all the errorhandling a pass the function to get the data as a parameter to that function.
So i have these functions:
GetAllSources() : List<Source>
GetAllSourcesByTaskId(int taskId) : List<Source>
GetAllSourcesByTaskIdPersonId(int taskId, int personId) : List<Source>
GetAllSourcesByDate(DateTime startDate, DateTime endDate): List<Source>
and i want be able to pass them as a parameter to a function.
How should i declare the called function?
ps
i've read this one
how to pass any method as a parameter for another function
but it uses an Action object which can't return anything (as far as i understand) and i want to return a List
This should work:
List<Source> WithErrorHandling(Func<List<Source>> func)
{
...
var ret = func();
...
return ret;
}
Usage:
var taskId = 123;
var res = WithErrorHandling(() => { GetAllSourcesByTaskId(taskId); });
You can pass a Func which can take many input parameters are return a value:
Func<T1, T2, TResult>
In your case something like this could work:
public List<Source> GetList(Func<List<Source>> getListMethod) {
return getListMethod();
}
Then call using
GetList(() => GetAllSources());
GetList(() => GetAllSourcesByTaskIdPersonId(taskId, personId));
Could you not just pass the List as an argument into your method, might be a bit tidier?
Well, you didn't say anything about the code inside these functions, but if you use linq inside, than your approach is definitely not the best one.
You should use something like this:
IQueriable<SomeType> GetAllSources()
{
return (from source in sources select ...);
}
IQueriable<SomeType> GetAllSourcesByTaskId(int taskId)
{
return (GetAllSources()).Where(source => source.TaskId == taskId);
}
I am developing tests for an application. There's a method that has a params array as a parameter. I have set up the method using Moq but when I run the test, the return value of the method is null, which means it is not being mocked.
Here's a code sample:
public interface ITicketManager {
string GetFirstTicketInQueueIfMatches(params string[] ticketsToMatch);
}
public class TicketManager : ITicketManager {
private Queue<string> ticketQueue = new Queue<string>();
public string GetFirstTicketInQueueIfMatches(params string[] ticketsToMatch) {
var firstQueuedTicket = ticketQueue.Peek();
var firstQueuedTicketMatchesAnyOfRequested = ticketsToMatch.Any(t => t == firstQueuedTicket);
if(firstQueuedTicketMatchesAnyOfRequested)
return firstQueuedTicket;
return null;
}
}
The mock code looks like this:
var mock = new Mock<ITicketManager>();
mock.Setup(m => m.GetFirstTicketInQueueIfMatches(It.IsAny<string>()))
.Returns(p => {
if(p.Contains("A"))
return "A";
return null;
});
Why is it never hitting the mocked method?
You're trying to call a method taking a single string, rather than an array. Bear in mind that it's the C# compiler which handles the params part, converting calling code which just specifies individual values into a call passing in an array. As far as the method itself is concerned, it's just getting an array - and that's what you're mocking.
The compiler is actually turning your code into:
mock.Setup(m => m.GetFirstTicketInQueueIfMatches
(new string[] { It.IsAny<string>() }))
which isn't what you want.
You should use:
mock.Setup(m => m.GetFirstTicketInQueueIfMatches(It.IsAny<string[]>()))
If you need to verify that it only gets given a single value, you'll need to do that in the same way you would for a non-params parameter.
Basically, params only makes a difference to the C# compiler - not to moq.
I believe the params string has to be matched by It.IsAny<string[]>() rather than It.IsAny<string>()
Using Moq, the code below works to setup a callback on a method with a params argument. Defining the second argument as an array does the trick.
MockLogger
.Setup(x => x.Info(It.IsAny<string>(), It.IsAny<object[]>()))
.Callback<string, object[]>((x, y) => _length = x.Length);
I'm using a class with a method that looks like the following:
public static T Get<T>(string key, Func<T> method)
{
//do stuff
var obj = method.Invoke();
return (T)obj
}
It works great if it I call it like this:
var x = Get<string>("mykey", test);
Where test is a function that has no parameters and returns a string. However, things break as soon as test has parameters. If I try:
var x = Get<string>("mykey", test(myparam));
I get the error "Argument type "String" is not assignable to parameter type "System.Func< string >".
I know the addition of (myparam) is the problem, but I'm not sure how it should be fixed. Is the issue with how the library's function is written or with how I'm trying to pass in the parameter?
var x = Get<string>("mykey", () => test(myparam));
You can call it like in the following sample code:
Get("mykey", () => test(myparam))
public static T Get<T>(string key, Func<T> method)
{
//do stuff
var obj = method.Invoke();
return (T)obj;
}
void Xyz()
{
int myparam = 0;
var x = Get("mykey", () => test(myparam)); // <string> is not needed
}
double test(int i)
{
return 0.0;
}
You need to curry the parameter by passing a lambda expression that takes no parameters and calls your function with a parameter from elsewhere:
var x = Get<string>("mykey", () => test(myparam));
It's how you're passing the parameter. test(myparam) has type String, and you need to pass a function which returns a String. You can make one with very little effort using a lambda expression:
var x = Get<string>("mykey", () => test(myparam));
The lambda expression () => foo creates a function which, when called, executes and returns foo.
You need to change your Func definition to define the input params thus:
Func<T,T1,T2,T3> method