How to pass a list of functions in parameters ? (UnityAction[]) - c#

I have a little problem with how to call my function.
Before, I only needed one parameters and i wrote the function like that (i absolutely need to call it like that) :
public void SetDialogs(UnityAction pFunctionClic){blablabla};
SetDialogs(() => MyClicFunction());
Now, i need to have multiple function in the parameters. So, i set my SetDialogs function like this :
public void SetDialogs(UnityAction[] pFunctionClic){blablabla};
Now, the problem is that i don't know what to write when i call my function.
Obviously
SetDialogs(() => [MyClicFunction()]);
doesn't work.
Can you help me please ?

I haven't used Unity before, but judging by what you're doing, you can pass an array of UnityAction as such:
SetDialogs(new UnityAction[] {
() => MyClickFunction(),
() => SomeOtherStuff(),
});

You can use a params array parameter. A params parameter must always be the last (or the only one) in the parameter list
public void SetDialogs(params UnityAction[] pFunctionClic){ ... };
Now you can pass it with as many UnityAction parameters as you need.
SetDialogs(() => MyClicFunction());
SetDialogs(() => MyClicFunction1(), () => MyClicFunction2());
SetDialogs(() => MyClicFunction1(), () => MyClicFunction2(), () => MyClicFunction3());
Inside the method you always see an array. When calling the method you don't need to pass it an array (C# converts the parameters to an array automatically); however, you can:
var unityActions = new UnityAction[] {
() => MyClicFunction1(),
() => MyClicFunction2(),
() => MyClicFunction3()
};
SetDialogs(unityActions);

Related

How does the compiler make delegate achieve lazy computation?

I am learning lazy computation, given by the code below
public static Func<R> Map<T, R>(this Func<T> f, Func<T, R> g)
{
return () => g(f());
}
because f could be a computing expensive function to generate T, that's why it is wrapped as Func<T>, but Map returns () => g(f()), where f() is more like a closure which has to be ready first, so it looks to me that f() will still be evaluated in () => g(f()), I know my understanding is wrong, but I couldn't figure what's wrong, so how does compiler kicks in and makes the code still lazy computation (i.e f() will not be called in () => g(f())?
() => g(f()) denotes an anonymous function, nothing more.
Nothing within that function is evaluated at the point of declaration.
f() is only invoked when that anonymous function is invoked, and is invoked every time the anonymous function is invoked.
() => g(f()) is an lambda expression which creates an anonymous function (which is actually a syntactic sugar translated by compiler - see #sharplab).
so it looks to me that f() will still be evaluated in () => g(f())
Yes, when the anonymous function represented by () => g(f()) will be called/evaluated, i.e. (pseudocde):
Func<_> x = () => g(f()); // nothing is evaluated here
x(); // f and g are evaluted here
So laziness is achieved by the fact that computation will be performed not when Map is called but when it's result is invoked, which is easy to check with side-effect. The following:
Func<int> returnsInt = () => 1;
Func<string> mapped = returnsInt.Map(i =>
{
Console.WriteLine("Inside mapping func");
return $"Int is: {i}";
});
// nothing printed yet
Console.WriteLine("Before the eval");
Console.WriteLine(mapped());
Gives next output:
Before the eval
Inside mapping func
Int is: 1

TargetParameterCountException when mocking function to return its input

I have a function that requires mocking out to return one of its parameters.
The function looks as follows:
IEnumerable<XDocument> WrapDocuments(MessageSettings messageSettings, IEnumerable<XDocument> documents);
I am mocking it like this:
mockDocumentWrapper
.Setup(m => m.WrapDocuments(It.IsAny<MessageSettings>(), It.IsAny<IEnumerable<XDocument>>()))
.Returns((IEnumerable<XDocument> x) => x);
When the test which invokes WrapDocuments() runs, I get a TargetParameterCountException.
What am I doing wrong here?
The delegate used in the Returns does not match the amount of parameters passed in the setup. The method expects 2 arguments so the delegate should also expect the same.
mockDocumentWrapper
.Setup(_ => _.WrapDocuments(It.IsAny<MessageSettings>(), It.IsAny<IEnumerable<XDocument>>()))
.Returns((MessageSettings m, IEnumerable<XDocument> docs) => docs);

Extracting lambda expression from LINQ

I have next chunk of code
var query = wordCollection.Select((word) => { return word.ToUpper(); })
.Where((word) =>
{
return String.IsNullOrEmpty(word);
})
.ToList();
Suppose I want to refactor this code and extract the lambda expression from Where clause. In Visual Studio I just select this lambda and do Refactor -> Extract Method. By doing so I have my LINQ modified to
var query = wordCollection.Select((word) => { return word.ToUpper(); })
.Where(NewMethod1())
.ToList();
and a NewMethod1() is declared as
private static Func<string, bool> NewMethod1()
{
return (word) =>
{
return String.IsNullOrEmpty(word);
};
}
The question is why this new method does NOT have any input parameters, as delegate Func states that NewMethod1() should have a string input parameter?
To get the expected result, mark just this part String.IsNullOrEmpty(word) and Extract the method:
private bool NewMethod(string word)
{
return String.IsNullOrEmpty(word);
}
What you originally got is because the extract created a method that returns a delegate. Not a method that matches the delegate. It is a method that returns another method. The latter accepts a string parameter word and returns a bool result.
Sure doing the above changes your code to:
.Where((word) => NewMethod(word))
But you can safely change that to:
.Where(NewMethod)
Side Note:
No need to use the return keyword in your Linq Queries or any one-line lambda, you can refactor you query to be like this:
var query = wordCollection.Select(word => word.ToUpper())
.Where(word => string.IsNullOrEmpty(word))
.ToList();
You are selecting the whole lambda, so it is trying to extract the whole lambda statement as a delegate that takes in a word and returns a boolean - Func < string, bool>.
When refactoring you should have only selected the "return String.IsNullOrEmpty(word);" part.
Additionally, you are using the lambas in an unnecessarily complex way.
You could refactor your LINQ statement to this:
var query = wordCollection.Select(word => word.ToUpper())
.Where(word => String.IsNullOrEmpty(word))
.ToList();
Or even to this:
var query = wordCollection.Select(word => word.ToUpper())
.Where(String.IsNullOrEmpty)
.ToList();

C# Action<> with Func<> parameter

I have the following method that I can't figure out correct syntax to call:
public T GetAndProcessDependants<C>(Func<object> aquire,
Action<IEnumerable<C>, Func<C, object>> dependencyAction) {}
I'm trying to call it like this:
var obj = MyClass.GetAndProcessDependants<int>(() => DateTime.Now,
(() => someList, (id) => { return DoSomething(x); }) }
Edited:
thx everyone, you guys helped turned on a light bulb in my head. here is what i did:
var obj = MyClass.GetAndProcessDependants<int>(
() => DateTime.Now,
(list, f) =>
{
list = someList;
f = id => { return DoSomething(id); };
});
not sure why i even an issue with this. it's one of those days i guess..
thx
Your lambda syntax is totally wrong.
You need to create a single lambda expression with two parameters:
(list, id) => DoSomething(...)
Right now the function is only accepting a single argument, when it asks for two!
You need to accept a list argument, such as (list, id) => {}
Just looking at the description above, it looks like the call should be:
var obj = MyClass.GetAndProcessDependants<int>(() => DateTime.Now,
(seq, fun) => { /* do something with seq and fun */ });
The key is since you are passing an Action that takes a Func, the caller is (most likely) going to be the one passing that Func into your Action. So you just specify how that Func is applied to the sequence passed in (if I'm reading the prototype correctly).
var obj = MyClass.GetAndProcessDependants<int>(
() => DateTime.Now,
(someList, id) => DoSomething(x)
);

Settings variable values in a Moq Callback() call

I think I may be a bit confused on the syntax of the Moq Callback methods. When I try to do something like this:
IFilter filter = new Filter();
List<IFoo> objects = new List<IFoo> { new Foo(), new Foo() };
IQueryable myFilteredFoos = null;
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Callback( (IFilter filter) => myFilteredFoos = filter.FilterCollection(objects))
.Returns(myFilteredFoos.Cast<IFooBar>());
This throws a exception because myFilteredFoos is null during the Cast<IFooBar>() call. Is this not working as I expect? I would think FilterCollection would be called and then myFilteredFoos would be non-null and allow for the cast.
FilterCollection is not capable of returning a null which draws me to the conclusion it is not being called. Also, when I declare myFilteredFoos like this:
Queryable myFilteredFoos;
The Return call complains that myFilteredFoos may be used before it is initialized.
This is because the code in the Returns method is evaluated immediately; that is, when the Setup method is being invoked.
However, the callback isn't being invoked until the GetByFilter method is invoked.
Luckily, the Returns method is overloaded so that you can defer its execution as well:
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Callback((IFilter filter) =>
myFilteredFoos = filter.FilterCollection(objects))
.Returns(() => myFilteredFoos.Cast<IFooBar>());
However, you don't need to save the value in a callback, because you can just get the parameter value directly in the Returns method:
mockObject.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Returns((IFilter filter) =>
filter.FilterCollection(objects).Cast<IFooBar>());
You can just take the parameter in the return value...
mockObject
.Setup(m => m.GetByFilter(It.IsAny<IFilter>()))
.Returns((IFilter filter) =>
{
myFilteredFoos = filter.FilterCollection(objects);
return myFilteredFoos.Cast<IFooBar>();
});

Categories