使用自定义 属性 属性将值映射到另一种类型
Use custom property attribute to map value to another type
我正在尝试将我的 MVVM 中的各种模型派生自的抽象 class 放在一起。部分用于抽象 IEditableObject/IChangeTracking 细节。
对于我的 IEditableObject,我想存储 "core data" 的浅表副本(一种定义将被序列化的数据的结构,通常用于数据库存储),以便可以取消或提交它。我不想做的是为我想出的每个新模型输入这个。
我已经定义了一个 [DataCoreItem] 自定义属性,我想用它来派生 class 的适用属性。由于一些不相关的原因,抽象 class 采用通用 DataCoreType 和 IDType:
public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{
public abstract DataCoreType Load(IDType id);
public abstract bool Save(DataCoreType dataCore);
public abstract bool Delete(IDType id);
// etc...
这是我的 CompanyModel 的示例
数据核心:
public struct CompanyDataCore
{
public int? ID;
public string Code;
public string Name;
public string PrimaryWebsite;
public string PrimaryPhone;
public string PrimaryEmail;
}
派生class:
public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int> {
CompanyDataCore dataCore;
[DataCoreMember]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
[DataCoreMember]
public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }
[DataCoreMember]
public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }
[DataCoreMember]
public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }
[DataCoreMember]
public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }
[DataCoreMember]
public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }
好吧,最后...我想要的摘要 class 是使用 BeginEdit()、EndEdit() 和 CancelEdit() 方法来处理数据备份副本的存储核心自动。这是我的设想:
[DataCoreMember(MemberName="ID")]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
// etc etc
在我的摘要中 class:
public virtual void BeginEdit() {
Type t = typeof(T);
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));
// WHAT TO DO HERE??? everything else looks good up to here
foreach (object o in props) {
this.dataCoreBackup.???? = o.value;
}
IsEditing = true;
}
如何将应用了DataCoreMember的属性映射到指定结构的属性?
我对反射(以及处理泛型类型)没有经验,但我认为这是可以做到的。我找到了一些示例(尚未尝试过),说明如何获取这些属性的列表以及应用了属性的列表,但我不确定如何基于此最终引用 DataCore 的 属性。谁能告诉我怎么做?非常感谢。
事实证明,使用 Reflection 是一项相当简单的任务。下面的解释(删除了很多不相关的代码)
这包含数据备份,因此支持取消编辑:
public struct CompanyDataCore
{
public int? ID;
public string Code;
public string Name;
public string PrimaryWebsite;
public string PrimaryPhone;
public string PrimaryEmail;
public string RootPath;
}
这里的属性 class 表示要备份哪些字段:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DataCoreMemberAttribute : Attribute
{
public string MemberName { get; set; }
}
这是派生的class:
public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int>
{
[Identifier]
[DataCoreMember(MemberName="ID")]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
[DataCoreMember(MemberName="Name")]
public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }
[DataCoreMember(MemberName="Code")]
public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }
[DataCoreMember(MemberName="PrimaryPhone")]
public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }
[DataCoreMember(MemberName="PrimaryEmail")]
public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }
[DataCoreMember(MemberName="PrimaryWebsite")]
public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }
}
这里是摘要 class:
public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{
protected DataCoreType dataCoreBackup;
public virtual void BeginEdit() {
Type t = typeof(T);
// get a the properties with the attribute
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));
// backup needs to be boxed because it's a struct
object boxedBackup = this.dataCoreBackup;
foreach (var prop in props) {
foreach (CustomAttributeData attribData in prop.GetCustomAttributesData()) {
if (attribData.Constructor.DeclaringType == typeof(DataCoreMemberAttribute)) {
object origValue = prop.GetValue(this);
FieldInfo field = boxedBackup.GetType().GetField(attribData.NamedArguments[0].TypedValue.Value.ToString());
field.SetValue(boxedBackup, origValue);
}
}
}
this.dataCoreBackup = (DataCoreType)boxedBackup;
IsEditing = true;
}
... 现在我可以在抽象 class 中处理 INotifiyPropertyChanged 和 IEditbaleObject,这样我就不必在我将要使用的每个特定模型中编写一堆管道。
希望其他人能发现这有用。
我正在尝试将我的 MVVM 中的各种模型派生自的抽象 class 放在一起。部分用于抽象 IEditableObject/IChangeTracking 细节。
对于我的 IEditableObject,我想存储 "core data" 的浅表副本(一种定义将被序列化的数据的结构,通常用于数据库存储),以便可以取消或提交它。我不想做的是为我想出的每个新模型输入这个。
我已经定义了一个 [DataCoreItem] 自定义属性,我想用它来派生 class 的适用属性。由于一些不相关的原因,抽象 class 采用通用 DataCoreType 和 IDType:
public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{
public abstract DataCoreType Load(IDType id);
public abstract bool Save(DataCoreType dataCore);
public abstract bool Delete(IDType id);
// etc...
这是我的 CompanyModel 的示例 数据核心:
public struct CompanyDataCore
{
public int? ID;
public string Code;
public string Name;
public string PrimaryWebsite;
public string PrimaryPhone;
public string PrimaryEmail;
}
派生class:
public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int> {
CompanyDataCore dataCore;
[DataCoreMember]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
[DataCoreMember]
public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }
[DataCoreMember]
public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }
[DataCoreMember]
public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }
[DataCoreMember]
public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }
[DataCoreMember]
public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }
好吧,最后...我想要的摘要 class 是使用 BeginEdit()、EndEdit() 和 CancelEdit() 方法来处理数据备份副本的存储核心自动。这是我的设想:
[DataCoreMember(MemberName="ID")]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
// etc etc
在我的摘要中 class:
public virtual void BeginEdit() {
Type t = typeof(T);
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));
// WHAT TO DO HERE??? everything else looks good up to here
foreach (object o in props) {
this.dataCoreBackup.???? = o.value;
}
IsEditing = true;
}
如何将应用了DataCoreMember的属性映射到指定结构的属性?
我对反射(以及处理泛型类型)没有经验,但我认为这是可以做到的。我找到了一些示例(尚未尝试过),说明如何获取这些属性的列表以及应用了属性的列表,但我不确定如何基于此最终引用 DataCore 的 属性。谁能告诉我怎么做?非常感谢。
事实证明,使用 Reflection 是一项相当简单的任务。下面的解释(删除了很多不相关的代码)
这包含数据备份,因此支持取消编辑:
public struct CompanyDataCore
{
public int? ID;
public string Code;
public string Name;
public string PrimaryWebsite;
public string PrimaryPhone;
public string PrimaryEmail;
public string RootPath;
}
这里的属性 class 表示要备份哪些字段:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DataCoreMemberAttribute : Attribute
{
public string MemberName { get; set; }
}
这是派生的class:
public class CompanyModel : ModelObject<CompanyModel, CompanyDataCore, int>
{
[Identifier]
[DataCoreMember(MemberName="ID")]
public int? ID { get { return dataCore.ID; } set { SetProperty(ref dataCore.ID, value); } }
[DataCoreMember(MemberName="Name")]
public string Name { get { return dataCore.Name; } set {SetProperty(ref dataCore.Name, value); } }
[DataCoreMember(MemberName="Code")]
public string Code { get { return dataCore.Code; } set { SetProperty(ref dataCore.Code, value); } }
[DataCoreMember(MemberName="PrimaryPhone")]
public string PrimaryPhone { get { return dataCore.PrimaryPhone; } set {SetProperty(ref dataCore.PrimaryPhone, value); } }
[DataCoreMember(MemberName="PrimaryEmail")]
public string PrimaryEmail { get { return dataCore.PrimaryEmail; } set { SetProperty(ref dataCore.PrimaryEmail, value); } }
[DataCoreMember(MemberName="PrimaryWebsite")]
public string PrimaryWebsite { get { return dataCore.PrimaryWebsite; } set { SetProperty(ref dataCore.PrimaryWebsite, value); } }
}
这里是摘要 class:
public abstract class ModelObject<T, DataCoreType, IDType> : INotifyPropertyChanged, IEditableObject
{
protected DataCoreType dataCoreBackup;
public virtual void BeginEdit() {
Type t = typeof(T);
// get a the properties with the attribute
var props = t.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(DataCoreMemberAttribute)));
// backup needs to be boxed because it's a struct
object boxedBackup = this.dataCoreBackup;
foreach (var prop in props) {
foreach (CustomAttributeData attribData in prop.GetCustomAttributesData()) {
if (attribData.Constructor.DeclaringType == typeof(DataCoreMemberAttribute)) {
object origValue = prop.GetValue(this);
FieldInfo field = boxedBackup.GetType().GetField(attribData.NamedArguments[0].TypedValue.Value.ToString());
field.SetValue(boxedBackup, origValue);
}
}
}
this.dataCoreBackup = (DataCoreType)boxedBackup;
IsEditing = true;
}
... 现在我可以在抽象 class 中处理 INotifiyPropertyChanged 和 IEditbaleObject,这样我就不必在我将要使用的每个特定模型中编写一堆管道。
希望其他人能发现这有用。