给定2个类型为T的对象A和B,我想将A中的属性值分配给B中的相同属性,而不对每个属性进行显式赋值.
我想保存这样的代码:
b.Nombre = a.Nombre;
b.Descripcion = a.Descripcion;
b.Imagen = a.Imagen;
b.Activo = a.Activo;
Run Code Online (Sandbox Code Playgroud)
做某事
a.ApplyProperties(b);
Run Code Online (Sandbox Code Playgroud)
可能吗?
Aze*_*ian 73
因为我相信Jon的版本太复杂而且Steve的版本太简单了,我喜欢Daniel对扩展类的想法.
另外一个通用版本很漂亮但不必要,因为所有项目都是对象.
我想自愿使用我的精益版和平均版.以上所有内容.:d
码:
using System;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Iterate the Properties of the source instance and
// populate them from their desination counterparts
PropertyInfo[] srcProps = typeSrc.GetProperties();
foreach (PropertyInfo srcProp in srcProps)
{
if (!srcProp.CanRead)
{
continue;
}
PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name);
if (targetProperty == null)
{
continue;
}
if (!targetProperty.CanWrite)
{
continue;
}
if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate)
{
continue;
}
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
continue;
}
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
{
continue;
}
// Passed all tests, lets set the value
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
/// <summary>
/// ExampleCopyObject
/// </summary>
/// <returns></returns>
public object ExampleCopyObject()
{
object destObject = new object();
this.CopyProperties(destObject); // inside a class you want to copy from
Reflection.CopyProperties(this, destObject); // Same as above but directly calling the function
TestClass srcClass = new TestClass();
TestStruct destStruct = new TestStruct();
srcClass.CopyProperties(destStruct); // using the extension directly on a object
Reflection.CopyProperties(srcClass, destObject); // Same as above but directly calling the function
//so on and so forth.... your imagination is the limits :D
return srcClass;
}
public class TestClass
{
public string Blah { get; set; }
}
public struct TestStruct
{
public string Blah { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
因为我很无聊,并且评论建议使用linq版本
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Collect all the valid properties to map
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeDest.GetProperty(srcProp.Name)
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
&& targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new { sourceProperty = srcProp, targetProperty = targetProperty };
//map the properties
foreach (var props in results)
{
props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 66
我有一个MiscUtil被调用的类型,PropertyCopy它做了类似的事情 - 虽然它创建了一个目标类型的新实例并将属性复制到那个中.
它不要求类型相同 - 它只是将所有可读属性从"源"类型复制到"目标"类型.当然,如果类型相同,那更有可能工作:)这是一个浅层副本,顺便说一句.
在本答案底部的代码块中,我扩展了该类的功能.要从一个实例复制到另一个实例,它PropertyInfo在执行时使用简单值 - 这比使用表达式树慢,但另一种方法是编写一个动态方法,我不太热.如果表现对你来说绝对至关重要,请告诉我,我会看到我能做些什么.要使用该方法,请编写如下内容:
MyType instance1 = new MyType();
// Do stuff
MyType instance2 = new MyType();
// Do stuff
PropertyCopy.Copy(instance1, instance2);
Run Code Online (Sandbox Code Playgroud)
(在哪里Copy使用类型推断调用的泛型方法).
我还没准备好完成一个完整的MiscUtil版本,但这里是更新的代码,包括注释.我不会为SO编辑器重新编写它们 - 只需复制整个块.
(如果我从头开始,我也可能在命名方面重新设计API,但我不想破坏现有用户......)
#if DOTNET35
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace MiscUtil.Reflection
{
/// <summary>
/// Non-generic class allowing properties to be copied from one instance
/// to another existing instance of a potentially different type.
/// </summary>
public static class PropertyCopy
{
/// <summary>
/// Copies all public, readable properties from the source object to the
/// target. The target type does not have to have a parameterless constructor,
/// as no new instance needs to be created.
/// </summary>
/// <remarks>Only the properties of the source and target types themselves
/// are taken into account, regardless of the actual types of the arguments.</remarks>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="source">Source to copy properties from</param>
/// <param name="target">Target to copy properties to</param>
public static void Copy<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
PropertyCopier<TSource, TTarget>.Copy(source, target);
}
}
/// <summary>
/// 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.
/// </summary>
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
/// <summary>
/// Copies all readable properties from the source to a new instance
/// of TTarget.
/// </summary>
public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
{
return PropertyCopier<TSource, TTarget>.Copy(source);
}
}
/// <summary>
/// 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.
/// Note that this type we do not have a constructor constraint on TTarget, because
/// we only use the constructor when we use the form which creates a new instance.
/// </summary>
internal static class PropertyCopier<TSource, TTarget>
{
/// <summary>
/// Delegate to create a new instance of the target type given an instance of the
/// source type. This is a single delegate from an expression tree.
/// </summary>
private static readonly Func<TSource, TTarget> creator;
/// <summary>
/// List of properties to grab values from. The corresponding targetProperties
/// list contains the same properties in the target type. Unfortunately we can't
/// use expression trees to do this, because we basically need a sequence of statements.
/// We could build a DynamicMethod, but that's significantly more work :) Please mail
/// me if you really need this...
/// </summary>
private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>();
private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>();
private static readonly Exception initializationException;
internal static TTarget Copy(TSource source)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
return creator(source);
}
internal static void Copy(TSource source, TTarget target)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
for (int i = 0; i < sourceProperties.Count; i++)
{
targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null);
}
}
static PropertyCopier()
{
try
{
creator = BuildCreator();
initializationException = null;
}
catch (Exception e)
{
creator = null;
initializationException = e;
}
}
private static Func<TSource, TTarget> BuildCreator()
{
ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
var bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
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.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is static 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)));
sourceProperties.Add(sourceProperty);
targetProperties.Add(targetProperty);
}
Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
}
}
}
#endif
Run Code Online (Sandbox Code Playgroud)
小智 21
建立史蒂夫的方法,我采用了扩展方法.这使用我的基类作为类型,但即使使用object作为param类型也应该可用.适合我的用途.
using System.Reflection;
//*Namespace Here*
public static class Ext
{
public static void CopyProperties(this EntityBase source, EntityBase destination)
{
// Iterate the Properties of the destination instance and
// populate them from their source counterparts
PropertyInfo[] destinationProperties = destination.GetType().GetProperties();
foreach (PropertyInfo destinationPi in destinationProperties)
{
PropertyInfo sourcePi = source.GetType().GetProperty(destinationPi.Name);
destinationPi.SetValue(destination, sourcePi.GetValue(source, null), null);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法如下:
item1.CopyProperties(item2);
Run Code Online (Sandbox Code Playgroud)
现在Item2与item1具有相同的属性数据.
Dan*_*iel 18
这是一个简短而甜蜜的版本,因为你说你的两个对象属于同一类型:
foreach (PropertyInfo property in typeof(YourType).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(targetObject, property.GetValue(sourceObject, null), null);
}
Run Code Online (Sandbox Code Playgroud)
修改Daniel版本以避免异常.
foreach (PropertyInfo property in typeof(YourType).GetProperties())
{
if (property.CanWrite)
{
property.SetValue(marketData, property.GetValue(market, null), null);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 7
这种简短而简单的扩展方法将允许您通过检查 Null 值并将匹配的属性从一个对象复制到另一个对象并且是可写的。
public static void CopyPropertiesTo(this object fromObject, object toObject)
{
PropertyInfo[] toObjectProperties = toObject.GetType().GetProperties();
foreach (PropertyInfo propTo in toObjectProperties)
{
PropertyInfo propFrom = fromObject.GetType().GetProperty(propTo.Name);
if (propFrom!=null && propFrom.CanWrite)
propTo.SetValue(toObject, propFrom.GetValue(fromObject, null), null);
}
}
Run Code Online (Sandbox Code Playgroud)
基本上在 2019 年,我们可能应该使用更多最新的语言功能,例如表达式树和编译的 lambda 表达式,而不是反射
由于我找不到满足我的要求(最重要的是速度)的“浅层克隆器”,我决定自己创建一个。它枚举所有 gettable/settable 属性,然后创建一个Block表达式,然后编译和缓存。这使它比流行的 AutoMapper 快了近 13 倍。用法很简单:
DestType destObject = PropMapper<SourceType, DestType>.From(srcObj);
Run Code Online (Sandbox Code Playgroud)
你可以在这里查看完整的源代码:https : //github.com/jitbit/PropMapper