Pass an action as a reference - c#

I'm trying to set an Action inside another Action, but the Action I pass to it is still null in the end. Here's a dumbed down example:
Action<Action<VerifylocalResult>> docollectprint = (vl) =>
{
vl = (vlocres) =>
{
//DoStuff
};
//This returns false, indicating the action has been set:
Console.WriteLine((vl == null).ToString());
};
//Hookups
docollectprint(vlocobj.Action_NotExists);
//This returns true, so the action has not been set:
Console.WriteLine((vlocobj.Action_NotExists==null).ToString());
I already tried passing a getter method instead of the real deal but the result is the same.. it's still null in the end.
Action<Func<Action<VerifylocalResult>>> docollectprint = (vl) =>
{
Action<VerifylocalResult> theaction = vl();
theaction = (vlocres) =>
{
//DoStuff
};
//This returns false, indicating the action has been set
Console.WriteLine((theaction == null).ToString());
};
//Hookups
docollectprint(() => { return vlocobj.Action_NotExists; });
//This returns true, so the action has not been set
Console.WriteLine((vlocobj.Action_NotExists==null).ToString());
Is there any way to do this? Also, sorry if this has been asked before, but when I searched all I found was people trying to do Action<ref string x> or something similar.
Update (solution):
Func<string, Action<VerifylocalResult>> docollectprint = (msg) =>
{
Action<VerifylocalResult> vl = (vlocres) =>
{
/*DoStuff*/
};
return vl;
};
//Hookups
vlocobj.Action_NotExists = docollectprint("x");

References are passed by value by default. This means that any changes to a reference are scoped locally only. You should be returning something rather than attempting to alter a passed in reference. From MSDN:
A variable that is captured will not be garbage-collected until the
delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in
the outer method.
A lambda expression cannot directly capture a ref [ByRef in VB] or
out parameter from an enclosing method.
A return statement in a lambda expression does not cause the
enclosing method to return.
A lambda expression cannot contain a goto statement, break statement,
or continue statement whose target is outside the body or in the
body of a contained anonymous function.

Let's get rid of all the actions, and try the same example with a more mundane object. Your code is basically equivalent with:
Action<string> changeString = (s) =>
{
s = "result";
Console.WriteLine(s);
};
string myString = "someString"
changeString(myString); //the reference is passed by value and won't work
changeString("someString"); //What should this do???
However, you could just return the changed thing, or in this case, just return the thing, as you are not doing anything with the value it's passed, along the line of:
Func<string> getString = () => return "result";
string myString = "someString";
myString = getString(); //works
or in your case:
Func<Action<VerifylocalResult>> docollectprint = () =>
{
return (vlocres) =>
{
//DoStuff
};
};
vlocobj.Action_NotExists = docollectprint();

To solve your problem you can use another delegate. First, declare it:
delegate void RefAction<T>(ref T reference);
Then change your method to this.
RefAction<Action<string>> docollectprint = (ref Action<string> vl) =>
{
vl = vlocres =>
{
//DoStuff
};
//Action has been set
Console.WriteLine((vl == null).ToString());
};
Action<string> action = null;
docollectprint(ref action);
//Action is still set
Console.WriteLine((action == null).ToString());
This, of course, in case you don't want to use Func for whatever reason.

It seems you want a Func<Action<VerifyLocalResult>>:
Func<Action<VerifylocalResult>> docollectprint = (vl) =>
{
vl = (vlocres) =>
{
//DoStuff
};
return vl;
};
then you can do
vlocobj.Action_NotExists = docollectprint();

Related

Simple syntax to create a record/tuple to pass few objects as a single parameter?

Consider use case: I need to pass a parameter to QueueUserWorkItem:
ThreadPool.QueueUserWorkItem((o) =>
{
var item = o as MyObject;
},
item);
Then requirements changed and I now need to pass 2 objects. So I would have to write something like:
ThreadPool.QueueUserWorkItem((o) =>
{
var items = o as Tuple<MyObject,MyObject2>;
},
new Tuple<MyObject,MyObject2>(item1, item2));
Is there a cleaner way to achieve this in C# 9+ ?
You can use this overload if your target framework has it. Then you can do:
ThreadPool.QueueUserWorkItem(args => {
var first = args.Item1; // it's of type string already
var second = args.Item2; // this is int
}, ("a", 1), false);
Or:
ThreadPool.QueueUserWorkItem(args => {
var first = args.first; // it's of type string already
var second = args.second; // this is int
}, (first: "a", second: 1), false); // name parameters here instead of using Item1 Item2
And in single parameter case you don't need to cast type from object (this overload accepts generic Action<T> delegate).
You can always leverage closures in C# lambdas and ingore passing parameter completely:
var item1 = ...;
var item2 = ...;
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine(item1);
Console.WriteLine(item2);
});
Also personally I would consider switching to Task.Run instead of ThreadPool.QueueUserWorkItem (though there should be some considiration due to some differences in behaviour).

returning different values in an NSubstitute mock method with an out parameter

Given a method with which to mock...
public bool TryReceive(out T message, TimeSpan millisecondsToWait)
I wish to set different messages on the first two calls, and return
true.
Subsequent calls return false.
I have tried a few variations, and in either case, the lambda expression is executed once, and never again. NSubstitute seems to be caching the first return value, and using the same value over and over.
I have tried this...
TCR #out;
var container = new AutoSubstitute();
var mb = container.Resolve<IMessageBuffer<TCR>>();
mb.TryReceive(out #out, Arg.Any<TimeSpan>()).Returns(
_ => { _[0] = buy; return true; },
_ => { _[0] = sell; return true; },
_ => { _[0] = null; return false; });
and I have tried this:
bool? bs = true;
TCR #out;
var container = new AutoSubstitute();
var mb = container.Resolve<IMessageBuffer<TCR>>();
mb.TryReceive(out #out, Arg.Any<TimeSpan>()).Returns(
_ =>
{
if (bs == true)
{
_[0] = buy;
bs = false;
return true;
}
if (bs == false)
{
_[0] = sell;
bs = null;
return true;
}
_[0] = null;
return false;
});
The only option I can think of is to provide a complete substitute implementation of the buffer for test purposes. My feeling is that given this documentation, it should be possible.
edit
I have been unable to get this working using NSubstitute, however if I provide a mock implementation of the IMessageBuffer<TCR> using
// mock buffer will return the necessary values by maintaining
// the relevant state internally.
container.Provide<IMessageBuffer<TCR>>(new MockBuffer());
it works correctly, so it's not a lifetimescope issue. Somehow NSubstitute seems to be calling the mocked out method only the first time, and reusing the value (or operating in such a way that it seems to reuse the value) - very strange.
NSubstitute struggles a bit with out and ref parameters.
The problem is that when we stub:
mb.TryReceive(out #out, Arg.Any<TimeSpan>()).Returns(...)
this will only execute when #out is the original value. This value will change the first time it is called, so the Returns lambda won't execute again (NSub thinks it is a different, non-matching call).
The easiest way to work-around this is to switch to ReturnsForAnyArgs(...):
mb.TryReceive(out #out, Arg.Any<TimeSpan>()).ReturnsForAnyArgs(action0, action1, action2);
This will work for all TryReceive calls, regardless of the parameter values, so the lambda should always execute. The downside of this is that if you want this to only run for specific values of the second argument then you'll have to put that logic inside the lambda (rather than using an argument matcher).

Returning Expression<> using various class properties

I have something like this:
public Expression<Func<Message, bool>> FilterData()
{
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.NotEqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
!message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.Contains:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) >= 0;
case FilterParameterOperatorEnum.DoesNotContain:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) == -1;
}
}
As you can see this is done on Message.Body
I now what to do the same thing on other string properties on the Message class and I don't want to duplicate all that code.
Is there a way to do that by passing in the property somehow?
Eclude the expression that retrieves the property value into a separate lambda expression:
public Expression<Func<Message, bool>> FilterData(Func<Message, string> retrievePropValueFunc)
In your filter expressions, you can then call that new lambda expression (just showing one as an example):
return message => !string.IsNullOrEmpty(retrievePropValueFunc(message))
&& retrievePropValueFunc(message)
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
To get to the Body property, pass message => message.Body to the retrievePropValueFunc parameter; as you see, you can modify this to pass different lambda expressions for the retrieval of other properties just as well.
You can try composing the expression completely manually, which will give you the ability to specify the property name as a string. This isn't a complete solution and it's untested, but it may give you a starting point to see what I mean:
var parameter = Expression.Parameter(typeof(Message), "o");
var getname = Expression.Property(parameter, "Body");
var isnullorempty = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, getname));
var compare = Expression.Equal(getname, Expression.Constant("thisvalue"));
var combined = Expression.And(isnullorempty, compare);
var lambda = Expression.Lambda(combined, parameter);
So you would change your function to accept "Body" as a parameter, and then cast the lambda at the end to:
expression<func<Message, bool>>
I may not have got the syntax for creating the lambda exactly right though.
Just change your function to receive the property instead of the message.
Or the hard way pass one more parameter for the property name and select it using reflection.
Edit just for one option than change for all the options.
public Func<Message, string, bool> FilterData()
{
return (message, propName) =>
{
var prop = message.GetType().GetProperty(propName);
if(prop != null){
var propValue = (string)prop.GetValue(message,null);
return !string.IsNullOrEmpty(propValue) && ...;
}
return false;
};
}
A very quick and dirty approach could be an enum plus some kind of magic:
public enum FilterTarget { Body, AnyOtherProp };
public Expression<Func<Message, bool>> FilterData(FilterTarget filterTarget)
{
string toBeFiltered = string.Empty;
switch(filterTarget)
{
case FilterTarget.Body :
{ toBeFiltered = message.Body; } break;
case FilterTarget.AnyOtherProp :
{ toBeFiltered = message.AnyOtherProp; } break;
default:
{
throw new ArgumentException(
string.Format("Unsupported property {0}", filterTarget.ToString()
);
}
}
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(toBeFiltered) &&
toBeFiltered.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
/* CUT: other cases are similar */
}
}
You can make the FilterData method even more fancy letting it accept a params FilterTarget[] thus gaining multi-filtering capabilities (off the top of my head, code not included).
Usage:
var aFilterDataResult = FilterData(FilterTarget.Body);
var anotherFilterDataResult = FilterData(FilterTarget.AnyOtherProp);
...

Storing and calling generically-typed delegates

What I'm looking for is probably not going to be possible without resorting to reflection. If that's the case, I'd still want to know the best way to pull it off.
Essentially, this is what I want my code to look like:
var instance = new MyClass();
instance.Add<int, string>(x => x.ToString());
instance.Add<string, Warehouse>(x => Warehouse.LookupByName(x));
instance.Add<Warehouse, IList<Supplier>>(x => x.Suppliers());
instance.Chain(3); // should call each lambda expression in turn
My question is, how can I store these delegates, each with a different signature, in a list in MyClass? And how can I call them later on when I want to, using the return value from each one as the input parameter to the next one?
The inside of MyClass may very well be a mess of List's and all that. But I'm not even sure where to start on this.
(Originally, I wanted to call new MyClass<int, string, Warehouse, IList<Supplier>>(). However, since there's no "type parameter array", I gave up on that approach.)
Well, you could store them all as Delegate - but the tricky thing is invoking them later.
If you're able to validate that the next delegate at any time is of the right type, e.g. by holding a Type reference for "the current output" you could always store a List<Func<object, object>> and make your Add method something like:
public void Add<TIn, TOut>(Func<TIn, TOut> func)
{
// TODO: Consider using IsAssignableFrom etc
if (currentOutputType != typeof(TIn))
{
throw new InvalidOperationException(...);
}
list.Add(o => (object) func((TIn) o));
currentOutputType = typeof(TOut);
}
Then to invoke them all:
object current = ...; // Wherever
foreach (var func in list)
{
current = func(current);
}
The Linq Select statement essentially does this...
var temp = instance.Select(x => x.ToString())
.Select(x => WareHouse.LookupByName(x))
.Select(x=> x.Suppliers());
List<List<Suppliers>> = temp.ToList(); //Evaluate statements
You can also store each intermediate Select call as an Enumerable to have the stated method you use in the OP.
class Program
{
static void Main(string[] args)
{
var instance = new MyClass();
instance.Add<int, string>(i => i.ToString());
instance.Add<string, int>(str => str.Length);
instance.Add<int, int>(i => i*i);
Console.WriteLine(instance.Chain(349));
Console.ReadLine();
}
}
public class MyClass
{
private IList<Delegate> _Delegates = new List<Delegate>();
public void Add<InputType, OutputType>(Func<InputType, OutputType> action)
{
_Delegates.Add(action);
}
public object Chain<InputType>(InputType startingArgument)
{
object currentInputArgument = startingArgument;
for (var i = 0; i < _Delegates.Count(); ++i)
{
var action = _Delegates[i];
currentInputArgument = action.DynamicInvoke(currentInputArgument);
}
return currentInputArgument;
}
}
If you want compile time type checking, what you are doing sounds suspiciously like plain old generic delegates. Assuming that there is some value to storing the individual functions that were Added (other than the Int to String conversion) and composing them later, you can do something like this:
var lookupWarehouseByNumber = new Func<int, Warehouse>(i => Warehouse.LookupByName(i.ToString()));
var getWarehouseSuppliers = new Func<Warehouse, IEnumerable<Supplier>>(w => w.Suppliers);
var getWarehouseSuppliersByNumber = new Func<int, IEnumerable<Supplier>>(i => getWarehouseSuppliers(lookupWarehouseByNumber(i)));

C# - Generic type function is trying to assign result to System.Func(specified type) when a function with parameters is passed in

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

Categories

Resources