8

まず第一に、私たちはおそらく、カスタムオブジェクト/エンティティ内にコピー機能を実装することが最善の方法であることに同意します。しかし、このシナリオを考えてみましょう。このオプションはありません。エンティティは将来変更されるため、コピー機能が失敗するため、エンティティの正確なコピーを作成する特定の関数を記述したくありません。

現在のエンティティの簡略版は次のとおりです。

[Serializable]
class MyEntity
{
    public MyEntity()
    { 
    }

    public MyEntity(int id, string name)
    {
        this.Id = id;
        this.Name = name; 
    }

    public int Id { get; set; }

    public string Name { get; set; }

    public MyEntity Copy()
    {
        throw new NotImplementedException();
    }
}

上記のすべての要件をカバーするために、私は2つの解決策を考え出しました。

        //original...
        MyEntity original = new MyEntity() { Id = 1, Name = "demo1" };

        //first way to copy object...
        List<MyEntity> list = new List<MyEntity>() { original};
        MyEntity copy1 = list.ConvertAll(entity => new MyEntity(entity.Id, entity.Name))[0];

        //second way to copy object...
        byte[] bytes = SerializeEntity(original);
        MyEntity copy2 = (MyEntity)DeserializeData(bytes);


    byte[] SerializeEntity(object data)
    {
        byte[] result = null;
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, data);
            result = ms.ToArray();
        }
        return result;
    }

    object DeserializeData(byte[] data)
    {
        object result = null;
        using(MemoryStream ms = new MemoryStream(data))
        {
           BinaryFormatter formatter = new BinaryFormatter();
           result = formatter.Deserialize(ms); 
        }
        return result;
    }

そして今、質問です。舞台裏で最も最適なソリューションはどれですか。また、その理由は、最初か2番目ですか。上記の要件を考慮して正確なコピーを行うためのより良い方法はありますか?コピーは大量に行われます。

PS注: 本座が指摘したように、最初の方法は基本的にすでにコピー機能であることを認識しています。私は、シリアル化のように用途が広く、カスタムコピー機能のように高速に近いものを探しています。

4

3 に答える 3

7

まず第一に、カスタム オブジェクト/エンティティ内に Copy 関数を実装するのが最善の方法であることに、おそらく誰もが同意するでしょう。

同意しません。私は毎回そのようなメソッドを書くのが嫌いです。拡張メソッドを使用した私の提案は次のとおりです。

public static T Copy<T>(this T obj)
    where T : class
{
    using (MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, obj);

        stream.Seek(0, SeekOrigin.Begin);
        return formatter.Deserialize(stream) as T;
    }
}

これは基本的に 2 番目のソリューションですが、わずかに最適化されています。MemoryStream をバイト配列にコピーして、そこから別の MemoryStream を作成する必要はありません。

[Serializable]最良の点は、属性を持つすべてのオブジェクトで使用できるジェネリックであることです。そして、各プロパティにアクセスする必要がある最初のソリューションよりも高速であると確信しています(測定はしていませんが)。

編集:

さて、実際に測定してみました。パフォーマンスに対する私の最初の仮定は完全に間違っていました!

ランダムな値で 1000000 個の MyEntity オブジェクトを作成し、それらをコピーしました (Honza Brestan深いコピーと浅いコピーに関するヒントも考慮しました)。

バイナリ フォーマッタを使用したディープ コピー: 14.727 秒
Copy メソッドを使用したディープ コピー: 0.490 秒
リフレクションを使用した
浅いコピー: 5.499 秒 Copy メソッドを使用した浅いコピー: 0.144 秒

于 2012-12-23T01:19:04.833 に答える
1

最初の試みと独自のCopyメソッドの作成の違いは何ですか?

public MyEntity Copy()
{
    return new MyEntity(this.Id, this.Name);
}

私にとって、これはとにかくまったく同じことを行うコレクションの試みよりも見栄えがよくなります。どちらの場合も、すべてのプロパティに明示的に名前を付ける必要があります。

エンティティクラス自体を変更できない場合でも、拡張メソッドを作成できます(コピーロジックを使用する場所から見える静的クラスに配置されます)

public static MyEntity Copy(this MyEntity source)
{
    return new MyEntity(source.Id, source.Name);
}

2回目の試みについては、2つの違いを考慮しましたか?それらはまったく同じではありません。1つ目は浅いコピーを作成し、2つ目(オブジェクトツリー全体がシリアル化可能である場合)は深いコピーを作成します。違いは、そのプロパティもコピーされるか、元のオブジェクトとそのコピーの両方が同じオブジェクトを参照するかです。同じことがペスコリーノのバージョンにも当てはまりますが、これは非常に見栄えがします。

したがって、問題は、どのコピーが必要/必要かということです。

真に動的な(しかしおそらくあまり効果的ではない)浅いコピー方法の場合、リフレクションを使用して、すべてのプロパティを列挙し、それらの値を元のオブジェクトからコピーにコピーする必要があると思います。不完全なデモバージョンは次のようになります。

public static MyEntity Copy(this MyEntity source)
{
    var result = new MyEntity();

    var properties = source.GetType().GetProperties(
          BindingFlags.Instance | BindingFlags.Public);

    foreach (var property in properties)
    {
        var val = property.GetValue(source, null);
        property.SetValue(result, val, null);
    }

    return result;
}

このアプローチには、パフォーマンス、特殊なケース(インデクサー、非公開プロパティなど)を処理する必要があるという独自の問題がありますが、作業は完了し、シリアル化できないオブジェクトでも機能します。一般的なバージョンも簡単に実行できます。必要かどうかはあなた次第です。

また、私とpescolinoの両方が拡張メソッドの使用を提案したため、問題が発生する可能性があることにも注意してください。エンティティCopyに拡張機能と同じシグネチャを持つメソッドが実際に含まれている場合、コンパイラは拡張機能の代わりにそれを使用することを決定します。NotImplementedException呼び出されたときに明らかにスローされます。したがって、これが当てはまる場合(そして、サンプルコードだけではない場合)、それは深刻な「落とし穴」であった可能性があります。このような場合の唯一の解決策は、拡張メソッドのシグネチャを変更することです。できれば名前を変更してください。

于 2012-12-23T01:45:28.630 に答える
1

AutoMapperを使用してみることができます:

Mapper.CreateMap<MyEntity, MyEntity>();

...

var copy3 = Mapper.Map<MyEntity, MyEntity>(original);
于 2012-12-23T04:28:35.930 に答える