Modelling Simple Calculations

I need to create a simple interactive single page application which allows a user to experiment with different financial scenarios. There are several variables which can be changed in the web page and the calculation should be re-computed.

I want to model the calculation as a series of smaller calculations and the final answer should be able to be computed with a single call to a compute method which cascades down through the calculation graph. Finally, if any of the sub-calculations are re-computed I want the possibility of other objects to be notified.

Having decided that the simplest calculation would consist of two inputs I have come up with this class:

public class TwoPredecessorCalculation
{
public Func<decimal> Predecessor1 { get; set; }
public Func<decimal> Predecessor2 { get; set; }

public Func<decimal, decimal, decimal> Calculation { get; set; }

public TwoPredecessorCalculation()
{

}

public TwoPredecessorCalculation(Func<decimal, decimal, decimal> calculation)
{
Calculation = calculation;
}

public decimal Compute()
{
decimal computedValue = Calculation(Predecessor1(), Predecessor2());
ValueComputed?.Invoke(this, new ComputeEventArgs{ ComputedValue = computedValue });
return computedValue;
}

public decimal Compute(Func<decimal> input1, Func<decimal> input2, Func<decimal, decimal, decimal> calculation)
{
Predecessor1 = input1;
Predecessor2 = input2;
Calculation = calculation;
return Compute();
}

public event EventHandler<ComputeEventArgs> ValueComputed;
}

Calculations can then be set up so:

Func<decimal> myHourlyRate = () => 15.00m;
Func<decimal> hoursWorked = () => 20m;

TwoPredecessorCalculation myNetCharge = new TwoPredecessorCalculation((mhr, hw) => mhr * hw);
myNetCharge.Predecessor1 = myHourlyRate;
myNetCharge.Predecessor2 = hoursWorked;

Func<decimal> vatRate = () => 23m;
TwoPredecessorCalculation myGrossCharge =
new TwoPredecessorCalculation((mnc, vatr) => mnc + (mnc * (vatr / 100)));
myGrossCharge.Predecessor1 = myNetCharge.Compute;
myGrossCharge.Predecessor2 = vatRate;

myGrossCharge.Compute().Should().Be(369m);
myNetCharge.Predecessor1 = () => 15.75m;

myGrossCharge.Compute().Should().Be(387.45m);

I think this is nice simple and somewhat fluent. I do realize that this kind of thing would be even better in F#.