Rand Random
Rand Random

Reputation: 7440

efficient way to check for changes in a "calculation" class

I have the following "calculation" class.

public class Foo
{

    private int? _sum = 0;

    public int Sum
    {
        get
        {
            if (_sum == null)
                _sum = 1 + 1; //simple code to show "some" calculation happens...

            return _sum.GetValueOrDefault();
        }
    }

}

In this example there is only 1 Field/Member but in my real class there are around 50 Members, that all look similar just with different value calculations.

In the class I also have a Recalc method.

This Recalc method does 4 things

  1. Save the old values
  2. set all fields to null
  3. calls the getter of every member
  4. Checks if the old values differ from the newvalues and does related stuff

I am not sure whats the best way to store the old values and check for changes with the new values.

My current implementation is this:

public string GetValuesKey()
{
    //again this method only handles the 1 Member and not all 50 in real app its string.Format("{0}|{1}|{2}|...{49}|{50}|", ....);
    return string.Format("{0}|", this.Sum);
}

public void Recalc()
{
    var oldValues = GetValuesKey();

    //set all fields to null
    //call the getters

    var newValues = GetValuesKey();
    if (oldValues != newValues)
    {
        //something changed...
    }
}

But with this code there is a memory/performance issue since I am doing boxing with the struct (decimal) writing to a reference type (string).

I kind of want to prevent doing 50 additional fields (something like _oldSum) for all the members. I just need to check if any member has changed during the Recalc procedure.

Just in Case, I cannot do the following code.

public void Recalc()
{
    var changes = false;

    var oldValue = this.Sum;
    _sum = null;
    var newValue = this.Sum;
    if (oldValue != newValue)
        changes = true;

    //check next member
    oldValue = this.Sum2;
    _sum2 = null;
    newValue = this.Sum2;
    if (oldValue != newValue)
        changes = true;

    //check next member and so on...
}

Since I need to set all fields to null first and only AFTER all of them have been set to null I can execute the getters, since the members are dependant on each other for exmple if the Sum Member would aggregate two other members and they havent been set to null first they would still have old values.

So I need a way to store something that represents all values before setting the fields null and after calling the getter of the members a way to check for changes.

Any help is welcome.

Edit:

Here is the code, I wrote to test performance/memory: http://pastebin.com/3WiNJHyS

Upvotes: 0

Views: 192

Answers (2)

Evk
Evk

Reputation: 101453

Instead of combining all values in a string and have some pefomance hit on that string construction - put all values in array (of decimal), then set all fields to null, make your calculation and compare arrays of old and new values.

Upvotes: 1

Massimiliano Kraus
Massimiliano Kraus

Reputation: 3835

If you don't want to write yourself all the 50 _oldValue fields, the only alternative is to use Reflection, that implies some boxing/unboxing, so performance will not be the best possible.

Anyway, in the following implementation I assume that in the Foo class the members that are involved in the calculation are all and the only ones that are properties of type decimal?. Otherwise, we need a more complicated solution, with BindingFlags, and/or Attribute on every field/property involved, and so on.

public void Recalc()
{
    var propertyInfos = GetType()
        .GetProperties()
        .Where(pInfo => pInfo.PropertyType.IsValueType);

    var fieldInfos = GetType()
        .GetFields()
        .Where(fInfo => fInfo.FieldType.IsValueType);

    //create a dictionary with all the old values
    //obtained from the backing fields.
    var oldValueDictionary = fieldInfos.ToDictionary(
        fInfo => fInfo.Name,
        fInfo => (decimal?)fInfo.GetValue(this));

    //set all properties to null
    foreach (var pInfo in propertyInfos)
        pInfo.SetValue(this, null);

    //call all the getters to calculate the new values
    foreach (var pInfo in propertyInfos)
        pInfo.GetValue(this);

    //compare new field values with the old ones stored in the dictionary;
    //if only one different is found, the if is entered.
    if (fieldInfos.Any(fInfo =>
        (decimal?)fInfo.GetValue(this) != oldValueDictionary[fInfo.Name]))
    {
        //do stuffs
    }
}

As a final note, your class configuration is very strange. Are you sure that setting all the calculations in the getters is the best choice? Maybe you should re-think about you design. One task is to retrieve a property value (a getter), another task is to calculate something (starting from some value stored in the backing fields)...

Upvotes: 1

Related Questions