5

何よりも、私は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た。しかし、それ以上の説明やリンクはありませんでした。このコードを高速化する方法を理解するのを手伝ってくれませんか? または、 、、およびレイト バインド デリゲートに関するリンクをいくつか提案していただけますか? または、私が対象とするのに役立つと思うものはありますか?ILGeneratorEmitILGenerator

完全な質問:

@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() { } 
4

4 に答える 4

6

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);
}

これがある場合、ジェネリックメソッドを書くのは簡単です。

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>、キャッシュからデリゲートを取得した後、のようなものを使用してデリゲートを適切なタイプにキャストする必要があることを意味します。

于 2012-03-18T21:40:02.687 に答える
4

ターゲットに一致するプロパティ(名前)のみを取得することを検討してください。これにより、コードが大幅に簡素化されます。

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 );
     }
}
于 2012-03-18T21:01:39.263 に答える
0

C#ReflectionIL-値のコピー方法を理解する

問題はクローン作成に関するものなので、ソースオブジェクトのタイプはターゲットオブジェクトのタイプと同じです(私が理解しているように、ソースとターゲットで異なるタイプを持つことができます)が、それでもこれは分析する価値があります。

于 2012-03-18T21:04:22.610 に答える
0

私はそれを行う方法についてブログ投稿を書きました (ポルトガル語のみですが、コードを読むことができました)

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

コードは次の場所から取得できます。

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

Reflection.Emit を使用するのは必要以上に難しいと思います。そこで、FluentIL (www.fluentil.org) と呼ばれるオープンソース ライブラリを作成して、作業を簡単にしました。ここで使いました。

[] 秒

于 2012-03-27T13:43:40.633 に答える