2013-06-25 33 views
1

我需要两个类似的物体(C#)之间的结合:C#结合数据对象

public class TypeA 
{ 
    public int I; 
    public string S; 
} 

public class TypeB 
{ 
    public IntField I; 
    public StringField S; 
} 

当类型A的变化的场我需要的TypeB更新匹配字段。

IntField是具有整数类型的值字段中,以使得更新的TypeB可以写为一个对象:

bInstance.I.Value = aInstance.I; 

如果正确地明白,如果我以结合的TypeB到类型A使用INotifyPropertyChanged的,它会造成样板:

aInstance.PropertyChanged += (sender, args) => 
{ 
    if (args.PropertyName == "I") 
     this.I.Value = sender.I; 
    if (args.PropertyName == "S") 
     this.S.Value = sender.S; 
}; 

另外:

  • 我访问的代码在这两个类型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) 
} 
+2

那么,你将不能够做任何事情* *,而他们仍然作为字段 - 我认为你可以把它们变成属性?你还可以描述(或更好地显示)'IntField' /'StringField'吗?他们是'struct'还是'class'? (即哪个?)这可能很重要。也;你究竟想要在这里做什么? 'IntField' /'StringField'的*目的是什么? –

+0

谢谢Marc!我可以将TypeA的字段设置为属性,并且我通过一些说明更新了问题。这有点疯狂,但我有与IntField和StringField的客户端 - 服务器数据绑定,现在我需要另一种类型的绑定,我应该使用一个新的服务器框架。 – Tom

回答

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; 
} 
+0

太棒了!我明白如何使用闭包和包装可以解决问题。 顺便说一句,你用“SetValue”方法钉住了它,我已经拥有了它。 :)你统治! 我很好奇这些技巧。源代码位于https://code.google.com/p/fast-member/? – Tom

+0

@Tom是的,那是来源;它使用每种类型的IL代(然后缓存并重新用于该类型的所有后来的事件);他们的关键在于它将基于字符串的成员访问转变为紧密的IL –