Кэширование дорогостоящих свойств или полей в записях C#

У меня есть запись C#, подобная следующей, со свойством, которое дорого вычислять:

sealed record MyAwesomeRecord(Dictionary<int, int> SomeDictionary)
{
    public int ExpensiveToComputeProperty => SomeDictionary.Sum(e => e.Value);

    //...
}

var original = new MyAwesomeRecord( new() { [0] = 0 });
var modified = original with { SomeDictionary = new() { [1] = 1 } };

Вместо того, чтобы повторно вычислять значение ExpensiveToComputeProperty при каждом доступе, я хотел бы вычислить его только один раз, после построения. Но, по-видимому, с записями C# конструктор больше не вызывается после модификации с помощью with. Я пробовал следующие способы исправить это:

  • Вместо этого используйте обычный class, но таким образом я больше не могу использовать синтаксис with, который я хотел бы продолжать использовать.
  • Сохраните ExpensiveToComputeProperty в обычном свойстве или поле. Это не работает, потому что он инициализируется один раз, а не после замены SomeDictionary на with.
  • Насколько я знаю, есть планы ввести хороший синтаксис, который позволил бы мне обновлять это свойство в C# 10. К сожалению, C# 10 еще не существует.

Есть ли способ использовать записи, чтобы избежать повторного выполнения дорогостоящих вычислений?


person Simon    schedule 30.01.2021    source источник
comment
Вместо использования установщика по умолчанию для свойства вы можете использовать настраиваемый установщик. В этом сеттере можно переделать дорогостоящий расчет.   -  person Xaver    schedule 30.01.2021
comment
@Xaver Отличная идея! Если вы опубликуете это как ответ, я приму это.   -  person Simon    schedule 30.01.2021
comment
Хорошо, я сделаю это (а также добавлю немного кода).   -  person Xaver    schedule 30.01.2021
comment
Не обязательно (для меня), но, возможно, приятно для других. Идея работает!   -  person Simon    schedule 30.01.2021


Ответы (1)


Вместо использования установщика по умолчанию для свойства вы можете использовать настраиваемый установщик. В этом сеттере вы можете сделать дорогостоящий расчет.

Вот полный пример (проверено и работает):

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    sealed record MyAwesomeRecord
    {
        public MyAwesomeRecord(Dictionary<int, int> SomeDictionary)
        {
            this.SomeDictionary = SomeDictionary;
        }

        public int ExpensiveToComputeProperty { get; private set; }

        private Dictionary<int, int>? _SomeDictionary;
        public Dictionary<int, int> SomeDictionary
        {
            get => _SomeDictionary!;
            set
            {
                _SomeDictionary = value;
                ExpensiveToComputeProperty = SomeDictionary.Sum(e => e.Value);
            }
        }

        //...
    }

    class Program
    {
        static void Main()
        {
            var original = new MyAwesomeRecord(new() { [0] = 0 });

            Console.WriteLine($"ExpensiveToComputeProperty = {original.ExpensiveToComputeProperty}");

            var modified = original with { SomeDictionary = new() { [1] = 1 } };

            Console.WriteLine($"ExpensiveToComputeProperty = {modified.ExpensiveToComputeProperty}");
        }
    }
}
person Xaver    schedule 30.01.2021