簡體   English   中英

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ãoDave的答案,我選擇了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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM