Как создавать свойства с «делегированными» средствами доступа?

#c# #properties #metaprogramming #accessor

#c# #свойства #метапрограммирование #средство доступа

Вопрос:

Я новичок в c # и ломал голову над этим пару дней. В принципе, я хочу создать тип свойства с логикой получения и установки, делегированной базовому типу, к которому принадлежит этот параметр.

Это всего лишь одно приложение: свойство, значение которого задается, скажем, реестром или каким-либо конфигурационным файлом.

  • Обработчик свойств в get будет выполнять что-то вроде проверки кэшированного значения (или нет), извлекать значение, если оно не кэшировано, кэшировать значение (или нет) и возвращать его.
  • Поведение установщика позволило бы только обработчику свойств устанавливать значение (если это возможно).

Есть предложения? Я думал об использовании DefaultPropertyAttribute , но я не совсем понимаю, как не писать всю необходимую логику с каждым средством доступа.


Похоже, это то, что я хочу: http://www.sharpcrafters.com/postsharp

«Писать меньше кода» Да. Все в порядке.

Ответ №1:

Я этим не горжусь:

 public abstract class HorribleBaseType
{
  private Lazy<string> _connectionString;
  private Action<string> _connectionStringSetter;
  private Func<string> _connectionStringGetter;

  public HorribleBaseType(
    Func<string> connectionStringGetter, 
    Action<string> connectionStringSetter)
  {
    _connectionStringGetter = connectionStringGetter;
    _connectionStringSetter = connectionStringSetter;

    _connectionString = new Lazy<string>(connectionStringGetter);
  }

  public string ConnectionString
  {
    get { return _connectionString.Value; }
    set 
    { 
      _connectionStringSetter(value);
      _connectionString = new Lazy<string>(_connectionStringGetter);
    }
  }
}

public class HorribleType : HorribleBaseType
{
  public HorribleType()
    : base(() => MyConfiguration.ConnectionString,
           (v) => MyConfiguration.ConnectionString = v) { }
}
  

100% непроверенный.

ОБНОВИТЬ Используя комбинацию вышеприведенного и ответа @hunter, вы могли бы сделать что-то вроде:

 public class DelegateProperty<T>
{
    #region Fields
    private readonly Func<T> _getter;
    private readonly Action<T> _setter;
    private Lazy<T> _lazy;
    #endregion

    #region Constructors
    public DelegateProperty(Func<T> getter, Action<T> setter)
    {
        _getter = getter;
        _setter = setter;

        _lazy = new Lazy<T>(getter);
    }
    #endregion

    #region Properties
    public T Value
    {
        get { return _lazy.Value; }
        set
        {
            _setter(value);
            _lazy = new Lazy<T>(_getter);
        }
    }
    #endregion

    #region Operators
    public static implicit operator T(DelegateProperty<T> prop)
    {
        return prop.Value; 
    }
    #endregion
}
  

С этим теперь вы можете сделать что-то вроде:

 class Program
{
    static void Main(string[] args)
    {
        string name = "Matt";
        var prop = new DelegateProperty<string>(
            () => name,
            value => name = value);

        var test = new Test(prop);
        Console.WriteLine(test.Name);

        test.Name = "Ben";
        Console.WriteLine(name);

        Console.ReadKey();
    }
}

public class Test
{
    private readonly DelegateProperty<string> NameProperty;

    public Test(DelegateProperty<string> prop)
    {
        NameProperty = prop;   
    }

    public string Name
    {
        get { return NameProperty; }
        set { NameProperty.Value = value; }
    }
}
  

Ответ №2:

Используя этот дурацкий класс:

 public class Property<T>
{
    Func<T> _func;
    T _value;
    bool _fetched;

    public Property(Func<T> func)
    {
        _func = func;
    }

    public T Value
    {
        get 
        {
            if (!_fetched)
            {
                _value = _func();
                _fetched = true;
            }
            return _value;
        }
        set { _value = value; }
    }
}
  

вы можете сделать что-то вроде этого:

 public class TestClass
{
    Property<int> _propertyInt;
    public int MyInt
    {
        get { return _propertyInt.Value; }
        set { _propertyInt.Value = value; }
    }

    Property<string> _propertyString;
    public string MyString
    {
        get { return _propertyString.Value; }
        set { _propertyString.Value = value; }
    }
}
  

Конечно, это не будет обрабатывать каждый случай, но это может вывести вас на «правильный» путь…

Комментарии:

1. Но у вас все равно был бы установщик общего назначения, не ограниченный ожидаемым типом поведения. По крайней мере, вы понимаете, что идея не в том, чтобы использовать базовый класс с таким поведением. Я не совсем против идеи общих установщиков, хотя и исхожу из опыта написания сценариев.