[英]Same Variable Names - 2 Different Classes - How To Copy Values From One To Another - Reflection - C#
[英]C# - copying property values from one instance to another, different classes
我有兩個C#類,它們具有許多相同的屬性(按名稱和類型)。 我希望能夠將Defect
實例中的所有非空值復制到DefectViewModel
的實例中。 我希望用反射來做,使用GetType().GetProperties()
。 我嘗試了以下方法:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
PropertyInfo[] defectProperties = defect.GetType().GetProperties();
IEnumerable<string> viewModelPropertyNames =
defectViewModel.GetType().GetProperties().Select(property => property.Name);
IEnumerable<PropertyInfo> propertiesToCopy =
defectProperties.Where(defectProperty =>
viewModelPropertyNames.Contains(defectProperty.Name)
);
foreach (PropertyInfo defectProperty in propertiesToCopy)
{
var defectValue = defectProperty.GetValue(defect, null) as string;
if (null == defectValue)
{
continue;
}
// "System.Reflection.TargetException: Object does not match target type":
defectProperty.SetValue(viewModel, defectValue, null);
}
最好的方法是什么? 我應該維護單獨的Defect
屬性列表和DefectViewModel
屬性,以便我可以執行viewModelProperty.SetValue(viewModel, defectValue, null)
嗎?
編輯:多虧了Jordão和Dave的答案,我選擇了AutoMapper。 DefectViewModel
位於WPF應用程序中,因此我添加了以下App
構造函數:
public App()
{
Mapper.CreateMap<Defect, DefectViewModel>()
.ForMember("PropertyOnlyInViewModel", options => options.Ignore())
.ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore())
.ForAllMembers(memberConfigExpr =>
memberConfigExpr.Condition(resContext =>
resContext.SourceType.Equals(typeof(string)) &&
!resContext.IsSourceValueNull
)
);
}
然后,我只是擁有以下行,而不是所有PropertyInfo
業務:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
看看AutoMapper 。
這很便宜而且容易。 它使用System.Web.Script.Serialization和一些擴展方法以方便使用:
public static class JSONExts
{
public static string ToJSON(this object o)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(o);
}
public static List<T> FromJSONToListOf<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<List<T>>(jsonString);
}
public static T FromJSONTo<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<T>(jsonString);
}
public static T1 ConvertViaJSON<T1>(this object o)
{
return o.ToJSON().FromJSONTo<T1>();
}
}
這是一些類似但不同的類:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string PetName { get; set; }
public int PetAge { get; set; }
public bool IsUgly { get; set; }
}
public class MemberV2
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string ChildName { get; set; }
public int ChildAge { get; set; }
public bool IsCute { get; set; }
}
以下是實施方法:
var memberClass1Obj = new Member {
Name = "Steve Smith",
Age = 25,
IsCitizen = true,
Birthday = DateTime.Now.AddYears(-30),
PetName = "Rosco",
PetAge = 4,
IsUgly = true,
};
string br = "<br /><br />";
Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON
var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>();
Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled
用這個替換你的錯誤行:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
您發布的代碼正在嘗試在DefectViewModel
對象上設置Defect
-tied屬性。
在組織代碼方面,如果你不想要像AutoMapper這樣的外部庫,你可以使用類似mixin的方案將代碼分開,如下所示:
class Program {
static void Main(string[] args) {
var d = new Defect() { Category = "bug", Status = "open" };
var m = new DefectViewModel();
m.CopyPropertiesFrom(d);
Console.WriteLine("{0}, {1}", m.Category, m.Status);
}
}
// compositions
class Defect : MPropertyGettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
class DefectViewModel : MPropertySettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
// quasi-mixins
public interface MPropertyEnumerable { }
public static class PropertyEnumerable {
public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) {
return self.GetType().GetProperties().Select(property => property.Name);
}
}
public interface MPropertyGettable : MPropertyEnumerable { }
public static class PropertyGettable {
public static object GetValue(this MPropertyGettable self, string name) {
return self.GetType().GetProperty(name).GetValue(self, null);
}
}
public interface MPropertySettable : MPropertyEnumerable { }
public static class PropertySettable {
public static void SetValue<T>(this MPropertySettable self, string name, T value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) {
self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach(
property => self.SetValue(property, other.GetValue(property)));
}
}
這樣,實現屬性復制的所有代碼都與使用它的類分開。 您只需要在其接口列表中引用mixins。
請注意,這不像AutoMapper那樣強大或靈活,因為您可能希望復制具有不同名稱的屬性或僅屬性的某些子集。 如果屬性不提供必要的getter或setter或它們的類型不同,它可能會徹底失敗。 但是,它仍然可能足以滿足您的目的。
首先,我不會將該代碼(某處)放在外部但是在ViewModel的構造函數中:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
如果這是唯一的類(或少數幾個),我不會進一步自動化,但寫出屬性分配。 自動化它看起來不錯,但可能會有比您預期的更多異常和特殊情況。
你有可能讓兩個類都實現一個定義共享屬性的接口嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.