最近思いついた、けどきっと誰かやってると思うアイディア。
よくあるBindableBaseはこんな感じ
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
問題点はプロパティはrefで使えないこと。
幸せになるためにこんなクラスを追加して(From ReactiveProperty)
public static class AccessorCache<TType>
{
private static readonly Dictionary<string, Delegate> getCache = new Dictionary<string, Delegate>();
private static readonly Dictionary<string, Delegate> setCache = new Dictionary<string, Delegate>();
public static Func<TType, TProperty> LookupGet<TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
var propertyName = GetPropertyName(propertySelector);
Delegate accessor;
lock (getCache)
{
if (!getCache.TryGetValue(propertyName, out accessor))
{
accessor = propertySelector.Compile();
getCache.Add(propertyName, accessor);
}
}
return (Func<TType, TProperty>)accessor;
}
private static string GetPropertyName<TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = propertySelector.Body as UnaryExpression;
if (unaryExpression == null) { throw new ArgumentException(nameof(propertySelector)); }
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null) { throw new ArgumentException(nameof(propertySelector)); }
}
return memberExpression.Member.Name;
}
public static Action<TType, TProperty> LookupSet<TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
var propertyName = GetPropertyName(propertySelector);
Delegate accessor;
lock (setCache)
{
if (!setCache.TryGetValue(propertyName, out accessor))
{
accessor = CreateSetAccessor(propertySelector);
setCache.Add(propertyName, accessor);
}
}
return (Action<TType, TProperty>)accessor;
}
private static Delegate CreateSetAccessor<TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
var propertyInfo = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;
var selfParameter = Expression.Parameter(typeof(TType), "self");
var valueParameter = Expression.Parameter(typeof(TProperty), "value");
var body = Expression.Assign(Expression.Property(selfParameter, propertyInfo), valueParameter);
var lambda = Expression.Lambda<Action<TType, TProperty>>(body, selfParameter, valueParameter);
return lambda.Compile();
}
}
こんなメソッドを作ってみる
protected bool SetProperty<TType, TProperty>(TType targetClass, Expression<Func<TType, TProperty>> selector, TProperty value, [CallerMemberName] String propertyName = null)
{
var oldValue = AccessorCache<TType>.LookupGet(selector).Invoke(targetClass);
if (object.Equals(oldValue, value)) return false;
AccessorCache<TType>.LookupSet(selector).Invoke(targetClass, value);
this.OnPropertyChanged(propertyName);
return true;
}
こんな感じに書ける
public class Hoge : BindableBase
{
private readonly TextView _textView;
public Hoge()
{
_textView = new TextView();
}
public string Text
{
get => _textView.Text;
set => SetProperty(_textView, tv => tv.Text, value);
}
}
ちょっと幸せになれるかもしれない。