C# Связывание объектов данных
Мне нужно связывание между двумя похожими объектами (C#):
public class TypeA
{
public int I;
public string S;
}
public class TypeB
{
public IntField I;
public StringField S;
}
Когда поле в TypeA изменяется, мне нужно обновить соответствующее поле в TypeB.
IntField - это объект, имеющий поле Value типа int, поэтому обновление TypeB можно записать в виде:
bInstance.I.Value = aInstance.I;
Если я правильно понимаю, если я использую INotifyPropertyChanged для привязки TypeB к TypeA, это вызовет шаблон:
aInstance.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "I")
this.I.Value = sender.I;
if (args.PropertyName == "S")
this.S.Value = sender.S;
};
Также:
- У меня есть доступ к коду в обоих типах, и я бы не хотел менять TypeB.
- У меня есть ~15 пар типов, таких как TypeA и TypeB - я бы хотел избежать шаблонов.
- Производительность очень важна, поэтому рефлексия не является предпочтительным вариантом.
- Возможно, статическое отражение является вариантом? Я слышал об этом, но я не уверен в:
- Как использовать это без шаблона.
- Его производительность.
- Использование его для разных экземпляров пар одного и того же типа (т.е. a1Instance->b1Instance, a2Intance->b2Instance и т. Д.).
Редактировать:
IntField - это класс. Он используется для другого типа привязки данных, который существует в системе (сложный, и вся система полагается на это). Он наследуется от класса, который представляет общее связываемое поле. вот часть этого:
public class IntField : GeneralField
{
private int _value;
public int Value
{
get { return _value; }
set
{
IsDirty = true;
_value = value;
}
}
// ... a couple of abstract method implementations go here (setting _value, and getting value in a non-type specific way)
}
1 ответ
Если вы не хотите много ручного кодирования, то лучшим выбором будет что-то основанное на рефлексии или метапрограммировании. Например:
static void Entwine(INotifyPropertyChanged source, object target)
{
source.PropertyChanged += (sender,args) =>
{
var prop = target.GetType().GetProperty(args.PropertyName);
if(prop != null)
{
var field = prop.GetValue(target) as GeneralField;
if(field != null)
{
var newVal = source.GetType().GetProperty(args.PropertyName)
.GetValue(source);
field.SetValue(newVal); // <=== some method on GeneralField
}
}
};
}
Во многих случаях это будет хорошо, но если отражение действительно является проблемой, могут помочь такие инструменты, как FastMember:
static void Entwine(INotifyPropertyChanged source, object target)
{
var sourceAccessor = ObjectAccessor.Create(source);
var targetAccessor = ObjectAccessor.Create(target);
source.PropertyChanged += (sender, args) =>
{
var field = targetAccessor[args.PropertyName] as GeneralField;
if (field != null)
{
var newVal = sourceAccessor[args.PropertyName];
field.SetValue(newVal);
}
};
}
Это значительно быстрее, чем отражение - он использует много хитростей, чтобы избежать боли. Это просто оставляет необходимость чего-то вроде:
abstract class GeneralField
{
// ...
public abstract void SetValue(object value);
}
class Int32Field : GeneralField
{
// ...
public override void SetValue(object value)
{
Value = (int)value;
}
}
И, конечно же, ваш INotifyPropertyChanged
реализация, например:
public class TypeA : INotifyPropertyChanged
{
private int i;
private string s;
public int I
{
get { return i; }
set { SetField(ref i, value); }
}
public string S
{
get { return s; }
set { SetField(ref s, value); }
}
private void SetField<T>(ref T field, T value,
[CallerMemberName]string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null) handler(
this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}