0

.NET FrameworkのICloneableインターフェイスは、通常、クラスのインスタンスのクローン作成をサポートする方法を提供します。

しかし、複数のサードパーティクラスがあり、各プロパティを個別に気にしたくない場合、それらのクラスのオブジェクトを効率的に複製するにはどうすればよいですか?(これらのクラスのソースコードは利用できません)。ジェネリックス拡張メソッドを使用する方法はありますか?

必要なのは、すべてのプロパティと(子)オブジェクトを含む正確なコピーを作成するディープクローンです。


例:LinqPadUserQueryでオブジェクトのクローンを作成するとします。

void Main()
{
    UserQuery uc=this;
    var copy=uc.CreateCopy(); // clone it
}

私が探しているのはCreateCopy()、のソースを所有していないため、このクラスの詳細を気にすることなくコピーを作成できる拡張機能ですUerQuery

(これUserQueryは私が必要とするものを示すための単なる例であり、PDFドキュメントクラス、ユーザーコントロールクラス、ADO.NETクラスなどである可能性があることに注意してください)。

4

2 に答える 2

7

私は現在、これに対して2つの異なるソリューションを持っています。1つはリフレクションを使用する場合と使用しない場合です。


1.)一般的な拡張メソッド(およびリフレクション)を使用すると、C#で次のように実行できます。

public static class Extension
{
    public static T CreateCopy<T>(this T src)
        where T: new()
    {
        if (src == null) return default(T); // just return null
        T tgt = new T(); // create new instance
        // then copy all properties
        foreach (var pS in src.GetType().GetProperties())
        {
            foreach (var pT in tgt.GetType().GetProperties())
            {
                if (pT.Name != pS.Name) continue;
                (pT.GetSetMethod()).Invoke(tgt, new object[] { 
                    pS.GetGetMethod().Invoke(src, null) });
            }
        };
        return tgt;
    } // method
} // class

これは非常に強力です。これは、作成したクラスからのオブジェクトだけでなく、.NETFrameworkのシステムクラスを含むすべてのクラスからのオブジェクトだけでなく、すべてのオブジェクトのクローンを作成できるためです。そして、リフレクションのおかげで、そのプロパティを知る必要はありません。それらは自動的にコピーされます。

このメソッドを使用するにCreateCopy()は、CustomerクラスとOrderクラスがあり、コピー(参照ではなく)を作成する必要があるが、新しいIDを使用する必要があるとします。次に、次のことを実行できます。

Order CopyOrderWithNewPK(Order item)
{
    Order newItem = item.CreateCopy(); // use ext. method to copy properties
    newItem.OrderId = new Guid(); // create new primary key for the item
    return newItem;
}

当然のことながら、Customerクラスの場合は同じように見えます。

Customer CopyCustomerWithNewPK(Customer item)
{
    Customer newItem = item.CreateCopy(); // use ext. method to copy properties
    newItem.CustomerId = new Guid(); // create new primary key for the item
    return newItem;
}

サンプルクラス内で定義されたすべてのプロパティの値が自動的にコピーされることに注意してください。ソースコードを所有していない場合は、サードパーティアセンブリのオブジェクトのクローンを作成することもできます。トレードオフは、リフレクションアプローチが遅いことです。


2.)この質問に触発されて、反省なしでそれを行う別の方法があります。1つの利点は、Entity Frameworkのオブジェクトのクローンを作成できることです(たとえば、エンティティオブジェクトを別のデータコンテキストにアタッチおよび再アタッチするため)。

// using System.Runtime.Serialization;
public class Cloner<T>
{
    readonly DataContractSerializer _serializer 
            = new DataContractSerializer(typeof(T));

    /// <summary>
    /// Clone an object graph
    /// </summary>
    /// <param name="graph"></param>
    /// <returns></returns>
    public T Clone(T graph)
    {
        MemoryStream stream = new MemoryStream();
        _serializer.WriteObject(stream, graph);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)_serializer.ReadObject(stream);
    }
}

上記の例を壊さないために、CreateCopy次のように拡張メソッドを変更できます。

public static class Extension
{   
    public static T CreateCopy<T>(this T src)
        where T: new()
    {
            return (new Cloner<T>()).Clone(src);
    }   
}   

注:Clonerを使用していSystem.Runtime.Serializationますが、複製されるオブジェクトはシリアル化可能である必要はありませ。これは利点になる可能性があります。私が見た他のソリューションでは、シリアル化可能なオブジェクトのみを複製できます。

于 2012-10-16T08:50:05.780 に答える
1

を実装すれば、ICloneable各プロパティを気にする必要はありません! あなたの答えは良いですが、私にとっては、ほとんどのコードとリフレクションのパフォーマンスが低い (!) ため、使用することを好みます

class Test : ICloneable
{
    public int A { get; set; }
    public int B { get; set; }

    #region ICloneable-Member

    public object Clone()
    {
        return base.MemberwiseClone();
    }

    #endregion
}
于 2012-10-16T08:57:59.517 に答える