-2

I want to save "instructions" for how a value should be determined at a later time, instead of saving the actual value at the current time. Is this even possible?

A simple C# example:

int[] myArray = new int[2];
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
//dictionary type can be changed if required

myArray[0] = 1;
myArray[1] = 2;

myDictionary.Add("total", (myArray[0] + myArray[1]) ); // Don't evaluate the value now

myArray[0] = 3;
myArray[1] = 4;

Console.WriteLine("total 2 = " + myDictionary["total"]); // Evaluate the value now
//Desired output: 7 (3+4), actual output = 3 (1+2)
Sebastian Norr
  • 7,686
  • 2
  • 12
  • 17

2 Answers2

1

You could use (expression bodied read-only) properties:

public int[] MyArray { get; set; }

public string CurrentResult => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";

You can use local functions if you need local variables:

string GetCurrentResult() => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";

MyArray[0] = 1;
MyArray[1] = 2;
Console.WriteLine(GetCurrentResult()); // total: 3 (1+2)
MyArray[0] = 3;
MyArray[1] = 4;
Console.WriteLine(GetCurrentResult()); // total: 7 (3+4)

If you don't use C#7 you could use a Func<string> delegate:

Func<string> GetCurrentResult = () => $"total: {MyArray.Sum()} ({string.Join("+", MyArray)})";
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

You are looking for Lazy<T>. It takes a Func<T> that doesn't evaluate until it is accessed via the Value property. Once it is evaluated, the result is stored for further access. So your code might look something like:

int[] myArray = new int[2];

var total = new Lazy<int>(() => myArray.Sum());
myArray[0] = 1;
myArray[1] = 2;
myArray[0] = 3;
myArray[1] = 4;

Console.WriteLine("total = " + total);
Console.WriteLine("total = " + total.Value);
Console.WriteLine("total = " + total);

The output of this code is:

total = Value is not created.
total = 7
total = 7

Note that, without calling total.Value, the result is not an int but rather a message telling us the expression hasn't been evaluated yet. Once total.Value has been called, subsequent accesses to total yield the value (due to implicit ToString() call in the Console.WriteLine()).

The benefit of using Lazy<T> is that the value is persisted, instead of being recalculated each time it is accessed. This makes it great for properties/fields in classes that may not be accessed each time the class is used but take a long time to generate a value.

Edit: Based on Op's feedback Lazy<T> isn't exactly what they are looking for.

If you always want to evaluate the expression each time it is accessed, you want a method or Func<T>. So imagine you have a class like this:

public class MyClass
{
    public int[] Vals {get;set;}
}

If you want to define a custom way of getting the (for example) sum of Vals, you have a couple of simple options.

A Class method

public class MyClass
{
    public int[] Vals {get;set;}
    public int SumOfVals()
    {
        return Vals.Sum();
    }
}

If you opt for the class method, you could (conceivably) make the class generic (MyClass<T>) and use a virtual/abstract method to implement the concrete SumOfVals method.

A Func<T> implemented in the Class

public class MyClass
{
    public int[] Vals {get;set;}
    public Func<int[], int> SumOfVals { get;set; }
}

Now you can set SumOfVals to some custom function each time the class is instantiated. If you don't set it to anything, you'll get a NullReferenceException if you try to do anything with it.

A Func<T> implemented inline

var vals = new int[2];
var sumVals = new Func<int[], int>((arr) => arr.Sum());
Console.WriteLine(sumVals(vals));

This is probably the most flexible, but this can lead to some spaghetti code. I would recommend just creating either a method in the class that calls into MyClass or creating a method in MyClass to handle this logic.

Pete Garafano
  • 4,863
  • 1
  • 23
  • 40
  • The problem with this solution is that it's only evaluated the first time, if I change the array and then run total.Value again, I still get the old value. It basically works as desired the first time, then it have the same problem as my first example code. – Sebastian Norr Mar 05 '18 at 17:28
  • It would have been a perfect solution if Lazy had some form of re-evaluation function, but I can't find any in the documentation. – Sebastian Norr Mar 05 '18 at 18:05