简体   繁体   English

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

[英]C# - copying property values from one instance to another, different classes

I have two C# classes that have many of the same properties (by name and type). 我有两个C#类,它们具有许多相同的属性(按名称和类型)。 I want to be able to copy all non-null values from an instance of Defect into an instance of DefectViewModel . 我希望能够将Defect实例中的所有非空值复制到DefectViewModel的实例中。 I was hoping to do it with reflection, using GetType().GetProperties() . 我希望用反射来做,使用GetType().GetProperties() I tried the following: 我尝试了以下方法:

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

What would be the best way to do this? 最好的方法是什么? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null) ? 我应该维护单独的Defect属性列表和DefectViewModel属性,以便我可以执行viewModelProperty.SetValue(viewModel, defectValue, null)吗?

Edit: thanks to both Jordão's and Dave's answers, I chose AutoMapper. 编辑:多亏了JordãoDave的答案,我选择了AutoMapper。 DefectViewModel is in a WPF application, so I added the following App constructor: 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
            )
        );
}

Then, instead of all that PropertyInfo business, I just have the following line: 然后,我只是拥有以下行,而不是所有PropertyInfo业务:

var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);

看看AutoMapper

This is cheap and easy. 这很便宜而且容易。 It makes use of System.Web.Script.Serialization and some extention methods for ease of use: 它使用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>();
    }
}

Here's some similiar but different classes: 这是一些类似但不同的类:

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

And here's the methods in action: 以下是实施方法:

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

Replace your erroneous line with this: 用这个替换你的错误行:

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

Your posted code is attempting to set a Defect -tied property on a DefectViewModel object. 您发布的代码正在尝试在DefectViewModel对象上设置Defect -tied属性。

In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this: 在组织代码方面,如果你不想要像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)));
  }
}

This way, all the code to achieve the property-copying is separate from the classes that use it. 这样,实现属性复制的所有代码都与使用它的类分开。 You just need to reference the mixins in their interface list. 您只需要在其接口列表中引用mixins。

Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. 请注意,这不像AutoMapper那样强大或灵活,因为您可能希望复制具有不同名称的属性或仅属性的某些子集。 Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. 如果属性不提供必要的getter或setter或它们的类型不同,它可能会彻底失败。 But, it still might be enough for your purposes. 但是,它仍然可能足以满足您的目的。

For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel: 首先,我不会将该代码(某处)放在外部但是在ViewModel的构造函数中:

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

And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. 如果这是唯一的类(或少数几个),我不会进一步自动化,但写出属性分配。 Automating it looks nice but there may be more exceptions and special cases than you expect. 自动化它看起来不错,但可能会有比您预期的更多异常和特殊情况。

你有可能让两个类都实现一个定义共享属性的接口吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 相同的变量名称 - 2个不同的类 - 如何将值从一个复制到另一个 - 反射 - C# - Same Variable Names - 2 Different Classes - How To Copy Values From One To Another - Reflection - C# 使用 C# 将数据从一个 dgv 复制到另一个具有值和计数的 dgv - Copying data from one dgv to another dgv with values and count using C# 使用C#上的参数从一个数据库复制行中的行 - Copying rows from one database from another with parameters on C# c# select 实例来自基于属性的类列表 - c# select instance from a list of classes based on property C#Linq从不同来源将值复制到模型中 - C# Linq copying values into model from different sources 使用C#将面板从一种形式复制到另一种形式 - Copying Panels from one Form to another in C# C#将文件从一台服务器复制到另一台具有权限的服务器 - C# Copying a file from one server to another with permissions 使用LINQ和C#将行从一个表复制到另一个表 - Copying a row from one table to another using LINQ and C# C#将多个XMLNode或XMLNodeList从一个XMLDocument复制到另一个 - C# Copying Multiple XMLNodes or XMLNodeList from One XMLDocument to Another C# 将枚举从一个 object 复制到另一个 - C# Copying Enumeration from one object to another
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM