Search code examples
c#design-patternsside-effects

How do I best capture possible side-effects of Func<..> in C#? Is there a best practice for this?


I have code from a maths library i am using, and cannot change. The function in question from the maths library I am using uses an approximation function iteratively, like this.

public double Calculate(double startInput, Func<double, double> approximationFunc)
{
    var result = 0d;
    var max = 10;
    for (int i = 0; i < max; i++)
    {
        result = approximationFunc(startInput);
        //do some checks ...
        startInput =DoSomething();
    }
    return result;
}

However, in the approximationFunc I am using 'in reality', I have to compute other things than the double result, and I need to re-use these results. The only thing I have come up with is:

 public void BusinessLogic(double startInput)
        {
            MyOtherResult myOtherResult = null;
            double myFunction(double input)
            {
                var result = ComputeMyResult(input);
                myOtherResult = ComputeMyOtherResult(result, someOtherStuff);
                return result;
            }

            var approximationResult = Calculate(startInput, myFunction);
            var myOtherApproximationResult = myOtherResult;
            // Do other stuff...
        }

However, I'm not sure if this the best way of getting the 'other result', and if there is a side-effects-free way of doing this. The solution I have come up with only works because I know that the library I use applies this function iteratively, and that's not ideal. How would you go about solving this in C#? I've been racking my brain for two days and it's not clicking.


Solution

  • A delegate can (and usually does) have a target object. So: you can intentionally craft your target object as the state wrapper that you need for your logic. For example:

    class MyState {
        public double MyFunc(double x) {
            // do whatever here, reading and writing to instance fields
            // on MyState
        }
    }
    ...
    var state = new MyState(/* additional values if needed */);
    var result = ctx.Calculate(42, state.MyFunc);