2010-08-31 120 views
4

我有兩個C#類,它們具有許多相同的屬性(按名稱和類型)。我希望能夠將Defect實例中的所有非空值複製到DefectViewModel的實例中。我希望用反射來做,使用GetType().GetProperties()。我試過以下內容:C# - 將屬性值從一個實例複製到另一個實例,不同的類

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'sDave's答案,我選擇了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); 

回答

0

任何機會,你可以有這兩個類都實現了一個定義共享屬性的接口?

+0

我想過。 'Defect'在外部庫中定義,我寧願不必修改它,因爲爲這些特定的共享屬性添加一個接口在DefectViewModel所在的庫的上下文中確實是有意義的。 – 2010-08-31 16:09:11

+0

這是有道理的。聽起來像你陷入了基於反思的解決方案之一。不過,我建議Henk關於使用構造函數的建議。 – 2010-08-31 18:06:42

2

這個替換您的錯誤路線:

PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name); 
targetProperty.SetValue(viewModel, defectValue, null); 

您發佈的代碼嘗試在DefectViewModel對象上設置Defect-tied屬性。

1

一兩件事,我不會把這些代碼(地方)的外部而是在視圖模型的構造:

class DefectViewModel 
{ 
    public DefectViewModel(Defect source) { ... } 
} 

如果這是唯一的類(或幾個中的一個),我不會自動它進一步寫出財產分配。自動化它看起來不錯,但可能會有比您期望的更多的異常和特殊情況。

+0

不確定我同意避免自動化,但我當然同意將代碼放入構造函數中。 – 2010-08-31 18:07:13

+0

我的意思是不避免,但更多'不過度使用' – 2010-08-31 19:06:15

+0

然後,我不能爭辯。儘管AutoMapper是一個有趣的通用解決方案,但它確實非常複雜,如果我們真正需要的是一個很少更改的硬編碼賦值的簡短列表。 – 2010-08-31 19:29:58

2

在組織代碼,如果你不希望像AutoMapper外部庫,你可以使用一個mixin-like方案的代碼中分離出這樣的條件:

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那樣健壯或靈活,因爲您可能想要複製具有不同名稱的屬性或只是屬性的一些子集。或者如果屬性沒有提供必要的獲取者或設置者或者他們的類型不同,它可能徹底失敗。但是,它仍然可能足以滿足您的需求。

2

這是便宜又容易。它使用System.Web。Script.Serialization和易於使用的一些extention方法:

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>(); 
    } 
} 

下面是一些similiar但不同類別:

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

不確定爲什麼這不被接受爲答案。我知道它具有嵌套屬性(IList等)的侷限性,但它起作用並且是一個非常簡單的答案。喜歡它。 – hal9000 2014-11-10 21:50:58

相關問題