한 개체의 속성 값을 동일한 유형의 다른 개체에 자동으로 적용 하시겠습니까?
2 개의 객체 A와 B가 유형 T 인 경우, 각 속성에 대해 명시 적으로 할당하지 않고 A의 속성 값을 B의 동일한 속성에 할당하고 싶습니다.
다음과 같은 코드를 저장하고 싶습니다.
b.Nombre = a.Nombre;
b.Descripcion = a.Descripcion;
b.Imagen = a.Imagen;
b.Activo = a.Activo;
같은 일을
a.ApplyProperties(b);
할 수 있습니까?
유사한 작업을 수행하는 전화 유형 이 있습니다. 대상 유형의 새 인스턴스를 만들고 속성을 여기에 복사합니다.MiscUtil
PropertyCopy
유형이 동일 할 필요는 없습니다. "source"유형에서 "target"유형으로 읽을 수있는 모든 속성을 복사하기 만하면됩니다. 물론 유형이 동일하면 작동 할 가능성이 더 많음. :) 얕은 사본, btw입니다.
이 답변의 맨 아래에있는 코드 블록에서 클래스의 기능을 확장했습니다. 한 인스턴스에서 다른 인스턴스로 복사하기 위해 실행 PropertyInfo
시간에 간단한 값을 사용합니다. 이것은 사용하는 것보다 느리지 만 대안은 동적 방법을 작성하는 것입니다. 성능이 절대적으로 중요하다면 주시면 무엇을 할 수 있는지 알아 보겠습니다. 방법을 사용하신 다음과 같이 작성하십시오.
MyType instance1 = new MyType();
// Do stuff
MyType instance2 = new MyType();
// Do stuff
PropertyCopy.Copy(instance1, instance2);
(여기서 Copy
유형 추론을 사용하여 호출되는 제네릭 메서드).
나는 완전한 MiscUtil을 할 준비가되어 있지는 않지만, 주석을 포함하고있는 업데이트 된 코드가 있습니다. SO 편집 기용으로 다시 래핑하지 꼭겠습니다. 전체 청크 만 복사하면됩니다.
(약간의 재 설계 할 수도 있고 사용자를 중단하고 싶지는 ...)
#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
나는 Jon의 버전이 너무 복잡하고 Steve의 버전이 너무 단순하다고 생각하기 때문에 확장 클래스에 대한 Daniel의 아이디어가 마음에.
모든 항목이 일반 버전은 예쁘지 만 모든 항목이 실행됩니다.
저의 야윈 버전을 자원하고 싶습니다. 위의 모든 것에 대한 거부. : 디
암호 :
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);
}
}
}
용법 :
/// <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; }
}
지루하고 댓글에서 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);
}
}
}
Steve의 방법을 기반으로 확장 방법 접근 방식을 사용했습니다. 이것은 내 기본 클래스를 유형으로 사용하는 클래스를 변수 유형으로 사용할 수 있습니다. 내 용도에 적합합니다.
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);
}
}
}
사용법은 다음과 가변합니다.
item1.CopyProperties(item2);
이제 Item2에는 item1과 동일한 속성 데이터가 있습니다.
두 개체가 모두 동일한 유형이라고 말하고 다음은 짧고 달콤한 버전입니다.
foreach (PropertyInfo property in typeof(YourType).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(targetObject, property.GetValue(sourceObject, null), null);
}
예외를 피하기 위해 Daniel 버전 수정.
foreach (PropertyInfo property in typeof(YourType).GetProperties())
{
if (property.CanWrite)
{
property.SetValue(marketData, property.GetValue(market, null), null);
}
}
내장 화를 사용하여 객체를 심층 복제 할 수 있습니다.
public static T DeepClone<T>(this T objectToClone) where T: BaseClass
{
BinaryFormatter bFormatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
bFormatter.Serialize(stream, objectToClone);
stream.Seek(0, SeekOrigin.Begin);
T clonedObject = (T)bFormatter.Deserialize(stream);
return clonedObject;
}
물론 클래스는 Serializable로 표시되어야합니다.
몇 년 동안 저는 ValueInjecter 라는 이름의 인기 라이브러리를 사용하고 있습니다.
너겟 : https://www.nuget.org/packages/ValueInjecter/
github : https://github.com/omuleanu/ValueInjecter
target.InjectFrom(source);
target.InjectFrom<Injection>(source);
target.InjectFrom(new Injection(parameters), source);
target.InjectFrom<Injection>(); // without source
기본 솔루션이 때문에 매우 간단하지만 (다른 답변 참조), 유지 관리 라이브러리를 사용하는 것이 더 좋기 많은 케이스 (예 : 깊은 복사, 제네릭, null 값) 및 최적화 (예 : 반사 된 속성 캐시)가 있습니다.
있다 ICloneable 과 object.MemberwiseClone (얕은 복사)가 (이 그렇게 요구 사항을 충족하지 않을 수 있습니다, 완전히 새로운 객체를 생성).
리플렉션을 사용하여 직접 수행 할 수 있습니다 (기본 클래스에서 상속하므로 다시 구현할 필요가 없음).
또는 코드로 생성 할 수 있습니다.
이런 식으로 시도해 볼 수 있습니다 ....
MyType destination = new MyType();
MyType source = new MyType();
// 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);
}
이 짧고 간단한 Extension 메서드를 사용하면 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);
}
}
기본적으로 2019 년에는 리플렉션 대신 표현식 트리 및 컴파일 된 람다 표현식과 같은 최신 언어 기능을 사용해야합니다.
내 요구 사항 (대부분의 속도)을 충족하는 "얕은 클론"을 찾을 수 없었기 때문에 직접 만들어보기로 결정했습니다. 모든 gettable / settable 속성을 열거 Block
한 다음 컴파일 및 캐시 되는 식 을 만듭니다 . 인기있는 AutoMapper보다 거의 13 배 더 빠릅니다. 사용법은 매우 간단합니다.
DestType destObject = PropMapper<SourceType, DestType>.From(srcObj);
여기에서 전체 소스를 볼 수 있습니다 : https://jitbit.github.io/PropMapper/
ApplyProperties와 같은 것을 원한다면 필요한 작업을 수행하는 확장 메서드를 Object에 작성할 수 있습니다. 그러한 확장 방법은 "순수"하거나 부작용이 없음을 인식하십시오. 그러나 능력이 필요하다면 그것을 달성하는 방법입니다.
이 게시물이 조금 오래되었을 수 있지만 답변은 좋지만 많은 객체를 대상 유형에 매핑해야 할 때 속성을 반복하는 것은 비용이 많이들 수 있습니다 (100 개의 소품 등을 상상).
이 AutoMapFactory 메서드를 사용합니다 (LinQ 만 필요), 속성에서 한 번만 반복되고 각 개체의 매핑이 빠릅니다 (100000 props / seconds).
private Func<S,T> AutoMapFactory<S,T>() where T: class, new() where S : class
{
List<Action<T, S>> mapActions = typeof(T).GetProperties().Where(tp => tp.CanWrite)
.SelectMany(tp => typeof(S).GetProperties().Where(sp => sp.CanRead)
.Where(sp => sp.Name == tp.Name && tp.PropertyType.IsAssignableFrom(sp.PropertyType))
.Select(sp => (Action<T,S>)((targetObj, sourceObj) =>
tp.SetValue(targetObj, sp.GetValue(sourceObj)))))
.ToList();
return sourceObj => {
if (sourceObj == null) return null;
T targetObj = new T();
mapActions.ForEach(action => action(targetObj, sourceObj));
return targetObj;
};
}
이것을 사용하는 방법 :
...
var autoMapper = AutoMapFactory<SourceType, TargetType>(); //Get Only 1 instance of the mapping function
...
someCollection.Select(item => autoMapper(item)); //Almost instantaneous
...
public TestClass {
public TestName {get;set;}
}
public void submain()
{
var originalTestClass = new TestClass()
{
TestName ="Test Name";
};
var newTestClass = new TestClass();
newTestClass.CopyPropertiesFrom(originalTestClass);
}
'ProgramingTip' 카테고리의 다른 글
any () 함수의 반대 (0) | 2020.11.19 |
---|---|
imdb.load_data () 함수에 대해 'allow_pickle = False'일 때 개체 배열을로드 할 수 없음을 수정하는 방법은 무엇입니까? (0) | 2020.11.19 |
이 C ++를 복사 할 수 있습니까? (0) | 2020.11.19 |
IllegalArgumentException : 도장 1의 바인드 값이 null입니다. (0) | 2020.11.19 |
가상 할당 연산자 C ++ (0) | 2020.11.18 |