Неизменяемые представления изменяемых типов

У меня есть проект, в котором мне нужно создать изрядное количество данных конфигурации, прежде чем я смогу выполнить процесс. На этапе настройки очень удобно, чтобы данные были изменяемыми. Однако, как только конфигурация будет завершена, я хотел бы передать неизменяемое представление этих данных функциональному процессу, поскольку этот процесс будет полагаться на неизменяемость конфигурации для многих своих вычислений (например, возможность предварительно вычислять вещи на основе при начальной конфигурации.) Я придумал возможное решение, используя интерфейсы для отображения представления только для чтения, но я хотел бы знать, сталкивался ли кто-нибудь с проблемами с этим типом подхода или есть другие рекомендации, как решить эту проблему.

Один из примеров шаблона, который я сейчас использую:

public interface IConfiguration
{
    string Version { get; }

    string VersionTag { get; }

    IEnumerable<IDeviceDescriptor> Devices { get; }

    IEnumerable<ICommandDescriptor> Commands { get; }
}

[DataContract]
public sealed class Configuration : IConfiguration
{
    [DataMember]
    public string Version { get; set; }

    [DataMember]
    public string VersionTag { get; set; }

    [DataMember]
    public List<DeviceDescriptor> Devices { get; private set; }

    [DataMember]
    public List<CommandDescriptor> Commands { get; private set; }

    IEnumerable<IDeviceDescriptor> IConfiguration.Devices
    {
        get { return Devices.Cast<IDeviceDescriptor>(); }
    }

    IEnumerable<ICommandDescriptor> IConfiguration.Commands
    {
        get { return Commands.Cast<ICommandDescriptor>(); }
    }

    public Configuration()
    {
        Devices = new List<DeviceDescriptor>();
        Commands = new List<CommandDescriptor>();
    }
}

РЕДАКТИРОВАТЬ

На основе данных, полученных от г-на Липперта и cdhowie, Я собрал следующее (удалил некоторые свойства для упрощения):

[DataContract]
public sealed class Configuration
{
    private const string InstanceFrozen = "Instance is frozen";

    private Data _data = new Data();
    private bool _frozen;

    [DataMember]
    public string Version
    {
        get { return _data.Version; }
        set
        {
            if (_frozen) throw new InvalidOperationException(InstanceFrozen);
            _data.Version = value;
        }
    }

    [DataMember]
    public IList<DeviceDescriptor> Devices
    {
        get { return _data.Devices; }
        private set { _data.Devices.AddRange(value); }
    }

    public IConfiguration Freeze()
    {
        if (!_frozen)
        {
            _frozen = true;
            _data.Devices.Freeze();
            foreach (var device in _data.Devices)
                device.Freeze();
        }
        return _data;
    }

    [OnDeserializing]
    private void OnDeserializing(StreamingContext context)
    {
        _data = new Data();
    }

    private sealed class Data : IConfiguration
    {
        private readonly FreezableList<DeviceDescriptor> _devices = new FreezableList<DeviceDescriptor>();

        public string Version { get; set; }

        public FreezableList<DeviceDescriptor> Devices
        {
            get { return _devices; }
        }

        IEnumerable<IDeviceDescriptor> IConfiguration.Devices
        {
            get { return _devices.Select(d => d.Freeze()); }
        }
    }
}

FreezableList , как и следовало ожидать, является замораживаемой реализацией IList . Это дает преимущества изоляции за счет некоторой дополнительной сложности.

7
задан Dan Bryant 13 November 2010 в 00:09
поделиться