2012-03-18 20 views
4

前もって、私はのAutoMapperについて知っています。私はそれを使いたくありません。私はC#を学んでいるので、それを深く見たいと思っています。だから私は自分自身でこの問題を解決しようとしています。1つのオブジェクトのプロパティ値を別のオブジェクトに転送する

しかし、プロパティと同じ名前と型を持ち、ソースから読み取り可能であり、ターゲットで書き込み可能な場合、あるタイプのプロパティの値を別のプロパティの値に対応させるためのプロパティコピーを作成しようとしています。私は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); 
     } 
    } 

それは動作しますが、私はSystem.Reflection.EmitILGeneratorを使用して遅延バインディング、デリゲートは、より迅速で、より高い性能を持っていることを、SOの答えを読む:サンプリング方法がここにあります。しかし、それ以上の説明やリンクはありませんでした。このコードをスピードアップする方法を理解できるように助けてください。またはEmitILGenerator、および遅延バインドの代表者に関するリンクを教えてもらえますか?それとも、私が題材にするのに役立つと思いますか?前もって感謝します。

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はモデルマッピングの文脈で(OPの紹介を参照してください)私は、ソースもターゲットも書き込み専用のプロパティを持っていないと考えることは容認できると思います。 – tvanfosson

0

C# Reflection IL - Understanding how values are copied

質問を約をクローニングされたので、ソースオブジェクトのタイプは、ターゲットオブジェクトの型と同じである(私は理解しますが、ソースとターゲットで異なる型を持つことができる一方、 )これはまだ分析する価値があるかもしれません。

5

これを行うにはReflection.Emitを使用しますが、通常はExpressionを使用する方が簡単です。基本的に同じパフォーマンスが得られます。パフォーマンスのメリットは、コンパイルされたコードをキャッシュする場合のみです(たとえば、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

関連する問題