代码之家  ›  专栏  ›  技术社区  ›  Sarah Vessels

C#-将属性值从一个实例复制到另一个不同的类

  •  4
  • Sarah Vessels  · 技术社区  · 14 年前

    我有两个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);
    }
    

    缺陷视图模型 所以我可以 viewModelProperty.SetValue(viewModel, defectValue, null)

    编辑: 多亏了他们俩 Jordão's Dave's 答案,我选择了AutoMapper。 缺陷视图模型 在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);
    
    7 回复  |  直到 8 年前
        1
  •  8
  •   Jordão    14 年前

    看一看 AutoMapper

        3
  •  2
  •   Jeff Mattfield Jeff Mattfield    14 年前

    将错误的行替换为:

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

    您发布的代码正在尝试设置 Defect -固定资产 DefectViewModel

        4
  •  2
  •   Jordão    14 年前

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

    这样,实现属性复制的所有代码都与使用它的类分开。您只需要在接口列表中引用mixin。

    请注意,这并不像AutoMapper那样健壮或灵活,因为您可能希望复制具有不同名称的属性或只是属性的某个子集。或者,如果属性没有提供必要的getter或setter或者它们的类型不同,那么它可能会彻底失败。但是,这对你来说还是足够的。

        5
  •  2
  •   GHP    12 年前

    这既便宜又容易。它利用了System.Web.Script脚本.序列化和一些扩展方法以便于使用:

    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
    
        6
  •  1
  •   Henk Holterman    14 年前

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

    如果这是唯一的类(或少数几个类之一),我不会进一步自动化它,而是写出属性赋值。自动化看起来不错,但可能有更多的例外和特殊情况比你预期的。

        7
  •  0
  •   Steven Sudit    14 年前