Конвертировать объект по отражению
Я хочу преобразовать объект A в объект B. Классы A и B имеют одинаковые свойства, только имена меняются.
Я использую этот метод:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
Как я могу редактировать свойства objectB
в foreach
цикл? Я хочу использовать универсальный метод для разных объектов.
3 ответа
Это решение обеспечивает как ваш путь отражения, так и альтернативный способ, определяя и реализуя метод копирования. CopyFrom
, Чтобы сократить код, вы можете сделать интерфейс базовым классом, чтобы вам не нужно было реализовывать CopyFrom
в подклассах....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
Я чувствую, что здесь может быть более глубокая проблема архитектуры. Я не представляю, почему вы хотите "скопировать" значения свойств из одного объекта класса в другой класс с теми же именами свойств.
Если вы пытаетесь "придать форму" объекту, возможно, просто передайте интерфейс.
В любом случае, посмотрите, поможет ли это:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Пример использования:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
При желании вы можете установить соответствие свойств для случая, когда свойства не имеют одно и то же имя. Также обратите внимание на использование атрибута IgnoredOnMorph, чтобы пометить свойства как недоступные для изменения (например, вычисляемые свойства)
Вы можете найти автоматическое использование здесь (см. https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
Вам нужно создать строку для каждого сопоставления объекта в файле запуска, чтобы настроить его, но если свойства будут одинаковыми, это будет так просто:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
И затем одна строка для разрешения сопоставления при необходимости
mapper.Map(objectOfClassA, new ClassB());