2012-03-18 2 views
4

Прежде всего, я знаю около AutoMapper, и я не хочу его использовать. Потому что я изучаю C#, и я хочу получить глубокий обзор. Поэтому я пытаюсь сам решить эту проблему (объяснил ниже).перенос значений одного объекта на другой

Тем не менее, я пытаюсь создать копир свойств, чтобы справиться с значениями свойств одного типа для другого, если свойство имеет то же имя и тип и читается из источника и может быть записано в цель. Я использую метод type.GetProperties(). Выборочный метод здесь:

static void Transfer(object source, object target) { 

     var sourceType = source.GetType(); 
     var targetType = target.GetType(); 

     var sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); 

     var targetProps = (from t in targetType.GetProperties() 
          where t.CanWrite 
           && (t.GetSetMethod().Attributes & MethodAttributes.Static) == 0 
          select t).ToList(); 

     foreach(var prop in sourceProps) { 
      var value = prop.GetValue(source, null); 
      var tProp = targetProps 
       .FirstOrDefault(p => p.Name == prop.Name && 
        p.PropertyType.IsAssignableFrom(prop.PropertyType)); 
      if(tProp != null) 
       tProp.SetValue(target, value, null); 
     } 
    } 

Это работает, но я прочитал ответ на SO, что использование System.Reflection.Emit и ILGenerator и конец переплета делегаты более быстро и имеет более высокую производительность. Но не было больше объяснений или ссылки. Можете ли вы помочь мне понять способы ускорения этого кода? или вы можете предложить мне некоторые ссылки о Emit, ILGenerator и делегаты с поздними переводами, пожалуйста? Или все, что вы думаете, поможет мне подчиниться? Заранее спасибо.

COMPELETE Q:

Я понимаю и узнать много вещей от @ svick отвечают. Но теперь, если я хочу использовать его как открытый общий метод, как я могу это сделать? что-то вроде этого:

public TTarget Transfer<TSource, TTarget>(TSource source) where TTarget : class, new() { } 

или расширение:

public static TTarget Transfer<TSource, TTarget>(this TSource source) where TTarget : class, new() { } 
+1

Я не думаю, что System.Reflection.Emit поможет вам здесь. В вашем случае как исходный, так и целевой объекты существуют во время компиляции, и вы просто копируете значения соответствующих свойств от одного к другому. Emit поможет вам, если вы захотите (например) создать целевой тип во время выполнения. –

ответ

3

Вы могли бы рассмотреть только получать свойства (по имени), что матч на цель. Это значительно упростит ваш код.

foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
{ 
    var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); 
    if (targetProperty != null 
      && targetProperty.CanWrite 
      && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) 
    { 
     targetProperty.SetValue(target, property.GetValue(source, null), null); 
    } 
} 
+0

Хорошая идея. Так спасибо –

+0

Я думаю, что ваш код не будет работать, если 'source' имеет некоторые свойства для записи. Они очень редки и являются плохой практикой, но, безусловно, возможны. – svick

+0

@svick в контексте сопоставления моделей (ref, введение OP) Я думаю, что было бы приемлемо предположить, что ни источник, ни цель не имеют только свойства записи. – tvanfosson

0

C# Reflection IL - Understanding how values are copied

Вопрос о клонировании поэтому тип объекта источника такой же, как тип целевого объекта (в то время, как я понимаю, вы можете иметь различные типы в источнике и цели), но все же это может стоить анализа.

5

Вы, можете использовать использовать Reflection.Emit, чтобы сделать это, но обычно намного проще использовать Expression s, и он дает вам в основном такую ​​же производительность. Имейте в виду, что преимущество в производительности зависит только от кеширования скомпилированного кода, например, в Dictionary<Tuple<Type, Type>, Action<object, object>>, который я здесь не делаю.

static void Transfer(object source, object target) 
{ 
    var sourceType = source.GetType(); 
    var targetType = target.GetType(); 

    var sourceParameter = Expression.Parameter(typeof(object), "source"); 
    var targetParameter = Expression.Parameter(typeof(object), "target"); 

    var sourceVariable = Expression.Variable(sourceType, "castedSource"); 
    var targetVariable = Expression.Variable(targetType, "castedTarget"); 

    var expressions = new List<Expression>(); 

    expressions.Add(Expression.Assign(sourceVariable, Expression.Convert(sourceParameter, sourceType))); 
    expressions.Add(Expression.Assign(targetVariable, Expression.Convert(targetParameter, targetType))); 

    foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
    { 
     if (!property.CanRead) 
      continue; 

     var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); 
     if (targetProperty != null 
       && targetProperty.CanWrite 
       && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) 
     { 
      expressions.Add(
       Expression.Assign(
        Expression.Property(targetVariable, targetProperty), 
        Expression.Convert(
         Expression.Property(sourceVariable, property), targetProperty.PropertyType))); 
     } 
    } 

    var lambda = 
     Expression.Lambda<Action<object, object>>(
      Expression.Block(new[] { sourceVariable, targetVariable }, expressions), 
      new[] { sourceParameter, targetParameter }); 

    var del = lambda.Compile(); 

    del(source, target); 
} 

Если у вас есть это, написание шаблонного метода является simpple:

public TTarget Transfer<TSource, TTarget>(TSource source) 
    where TTarget : class, new() 
{ 
    var target = new TTarget(); 
    Transfer(source, target); 
    return target; 
} 

Это может иметь смысл, чтобы сделать основной рабочий метод родовым слишком и создать Action<TSource, TTarget>, или даже пусть он непосредственно создать объект и используйте Func<TSource, TTarget>. Но если добавить кеширование, как я предложил, это означало бы, что вам нужно будет использовать что-то вроде Dictionary<Tuple<Type, Type>, Delegate> и передать делегат в нужный тип после извлечения его из кеша.

+0

Я почти понимаю ваш код; Но из-за того, что я студент-новичок на C#, кое-что еще нечетко для меня. Можете ли вы объяснить, как я могу использовать это для открытых дженериков? Что-то вроде этого: 'TTarget Transfer (TTarget target), где TTarget: class, new()' Я поставил это на оригинальный вопрос. Большое спасибо –

+0

@ king.net, см. Править. – svick

+0

Благодаря редактированию и руководству. Да, я понимаю, о чем вы говорите, и я знаю об этом. Но мой вопрос заключается в получении типов и свойств для общих типов и как я могу создать выражение для них. Например, это выглядит так: var sourceParameter = Expression.Parameter (typeof (object), "source"); var targetParameter = Expression.Parameter (typeof (object), "target"); 'не требуется, если метод является открытым общим методом. Верно? –

0

я написал в блоге о том, как это сделать (португальский только, но вы можете прочитать код)

http://elemarjr.net/2012/02/27/um-helper-para-shallow-cloning-emitting-em-c/

Вы можете получить код от:

https://github.com/ElemarJR/FluentIL/blob/master/demos/Cloning/src/Cloning/Cloning/ILCloner.cs

Я думаю, что использовать Reflection.Emit сложнее, чем нужно. Итак, я написал библиотеку с открытым исходным кодом под названием FluentIL (www.fluentil.org), чтобы упростить ее работу. Я использовал его здесь.

[] s

Смежные вопросы