当前位置:  开发笔记 > 编程语言 > 正文

如何在c#中使用相同的属性名将值从类X复制到类Y?

如何解决《如何在c#中使用相同的属性名将值从类X复制到类Y?》经验,为你挑选了3个好方法。

假设我有两个类:

public class Student
{
    public int Id {get; set;}
    public string Name {get; set;}
    public IList Courses{ get; set;}
}

public class StudentDTO
{
    public int Id {get; set;}
    public string Name {get; set;}
    public IList Courses{ get; set;}
}

我想将Student类中的值复制到StudentDTO类:

var student = new Student();
StudentDTO studentDTO = student;

我怎么能通过反思或其他解决方案来做到这一点?



1> Marc Gravell..:

列表使它变得棘手...我之前的回复(下面)仅适用于类似的属性(不是列表).我怀疑你可能只需要编写和维护代码:

    Student foo = new Student {
        Id = 1,
        Name = "a",
        Courses = {
            new Course { Key = 2},
            new Course { Key = 3},
        }
    };
    StudentDTO dto = new StudentDTO {
        Id = foo.Id,
        Name = foo.Name,
    };
    foreach (var course in foo.Courses) {
        dto.Courses.Add(new CourseDTO {
            Key = course.Key
        });
    }

编辑; 仅适用于拷贝 - 不是列表

反思是一种选择,但速度很慢.在3.5中,您可以将其构建为编译的代码Expression.Jon Skeet在MiscUtil中有一个预先推出的样本- 只需用作:

Student source = ...
StudentDTO item = PropertyCopy.CopyFrom(student);

因为这使用了编译Expression它会大大超出反射.

如果您没有3.5,则使用反射或ComponentModel.如果你使用ComponentModel,你至少可以用HyperDescriptor得到它几乎一样快Expression

Student source = ...
StudentDTO item = new StudentDTO();
PropertyDescriptorCollection
     sourceProps = TypeDescriptor.GetProperties(student),
     destProps = TypeDescriptor.GetProperties(item),
foreach(PropertyDescriptor prop in sourceProps) {
    PropertyDescriptor destProp = destProps[prop.Name];
    if(destProp != null) destProp.SetValue(item, prop.GetValue(student));
}



2> AboutDev..:

好的,我只是查看了Marc发布的MiscUtil,它非常棒.我希望马克不介意我在这里添加代码.

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class Program
    {
        public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList Courses { get; set; }
            public static implicit operator Student(StudentDTO studentDTO)
            {
                return PropertyCopy.CopyFrom(studentDTO);
            }
        }

        public class StudentDTO
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList Courses { get; set; }
            public static implicit operator StudentDTO(Student student)
            {
                return PropertyCopy.CopyFrom(student);
            }
        }


        static void Main(string[] args)
        {
            Student _student = new Student();
            _student.Id = 1;
            _student.Name = "Timmmmmmmmaaaahhhh";
            _student.Courses = new List();
            _student.Courses.Add(101);
            _student.Courses.Add(121);

            StudentDTO itemT = _student;

            Console.WriteLine(itemT.Id);
            Console.WriteLine(itemT.Name);
            Console.WriteLine(itemT.Courses.Count);
        }


    }


    // COOLEST PIECE OF CODE FROM - http://www.yoda.arachsys.com/csharp/miscutil/

    /// 
    /// Generic class which copies to its target type from a source
    /// type specified in the Copy method. The types are specified
    /// separately to take advantage of type inference on generic
    /// method arguments.
    /// 
    public class PropertyCopy where TTarget : class, new()
    {
        /// 
        /// Copies all readable properties from the source to a new instance
        /// of TTarget.
        /// 
        public static TTarget CopyFrom(TSource source) where TSource : class
        {
            return PropertyCopier.Copy(source);
        }

        /// 
        /// Static class to efficiently store the compiled delegate which can
        /// do the copying. We need a bit of work to ensure that exceptions are
        /// appropriately propagated, as the exception is generated at type initialization
        /// time, but we wish it to be thrown as an ArgumentException.
        /// 
        private static class PropertyCopier where TSource : class
        {
            private static readonly Func copier;
            private static readonly Exception initializationException;

            internal static TTarget Copy(TSource source)
            {
                if (initializationException != null)
                {
                    throw initializationException;
                }
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                return copier(source);
            }

            static PropertyCopier()
            {
                try
                {
                    copier = BuildCopier();
                    initializationException = null;
                }
                catch (Exception e)
                {
                    copier = null;
                    initializationException = e;
                }
            }

            private static Func BuildCopier()
            {
                ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
                var bindings = new List();
                foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
                {
                    if (!sourceProperty.CanRead)
                    {
                        continue;
                    }
                    PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
                    if (targetProperty == null)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.CanWrite)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
                    }
                    bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
                }
                Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
                return Expression.Lambda>(initializer, sourceParameter).Compile();
            }
        }
    }

}



3> Kuncevič..:

FYI

当我遇到同样的问题时,我发现了AutoMapper(http://automapper.codeplex.com/)然后在阅读了AboutDev的答案后,我做了一些简单的测试,结果令人印象深刻

这里的测试结果如下:

测试自动映射器:22322毫秒

测试隐式算子:310毫秒

测试属性副本:250毫秒

测试发射映射器:281毫秒

我想强调它只是样本(StudentDTO,Student),它只有几个属性,但如果类有50-100个属性会发生什么,我猜它会显着影响性能.

此处有更多测试详细信息: .net中的对象复制方法:自动映射器,发射映射器,隐式操作,属性复制


你是怎么编译的?
推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有