web-dev-qa-db-ja.com

オブジェクトを複製する方法

以下を実行すると.. Person bに対して行われた操作は、Person aを変更します(これを行うと、Person aからPerson bが複製されると考えました)。また、リンク後に個人aを変更すると個人bが変更されるかどうかもわかりません。私のコードのおかげで、これは一方向にしか見えません。

Person a = new Person() { head = "big", feet = "small" };
Person b = a; 

b.head = "small"; //now a.head = "small" too   

代わりにこれを行うと、人aは完全に分離されます。

Person b = new Person() { head = a.head, feet = a.feet };

この動作をC#の他の動作と比較するとき、このすばらしい動作が意味をなします。しかし、これは大きなオブジェクトでは非常に迷惑になります。

これをショートカットする方法はありますか?

といった:

Person b = a.Values;

50
PiZzL3

これをショートカットする方法はありますか?

いいえ、そうでもありません。オリジナルが「コピー」に影響しないように、新しいインスタンスを作成する必要があります。これにはいくつかのオプションがあります。

  1. タイプがstructではなくclassである場合、(インスタンスへの参照を単にコピーするのではなく)値によってコピーされます。これにより、説明しているセマンティクスが得られますが、望ましくない傾向がある他の多くの副作用があり、可変タイプには推奨されません(これは明らかにそうであるか、これは問題ではありません!)

  2. タイプに「クローニング」メカニズムを実装します。これはICloneableでも、インスタンスを取得してそこから値をコピーする単なるコンストラクターでもかまいません。

  3. リフレクション、MemberwiseCloneなどを使用してすべての値をコピーするため、これを行うためにコードを記述する必要はありません。特に非単純型を含むフィールドがある場合、これには潜在的な問題があります。

39
Reed Copsey

あなたが探しているのはクローニングです。 IClonable を実装してから、クローンを作成する必要があります。

例:

class Person() : ICloneable
{
    public string head;
    public string feet; 

    #region ICloneable Members

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

    #endregion
}

次に、Cloneメソッドを呼び出して、ShallowCopy(この特定のケースではDeepCopy

Person a = new Person() { head = "big", feet = "small" };
Person b = (Person) a.Clone();  

Objectクラスの MemberwiseClone メソッドを使用して、クローンを作成できます。

63
Shekhar_Pro

これにはAutoMapperを使用します。それはこのように動作します:

Mapper.CreateMap(typeof(Person), typeof(Person));
Mapper.Map(a, b);

これで、人aは人bのすべてのプロパティを持ちます。

余談ですが、AutoMapperはさまざまなオブジェクトに対しても機能します。詳細については、 http://automapper.org をご覧ください。

更新:この構文を使用します(単純に-実際にCreateMapsはAutoMapperプロファイルにあります):

Mapper.CreateMap<Person, Person>;
Mapper.Map(a, b);

同じタイプのオブジェクトを別のオブジェクトにマッピングするためにCreateMapを実行する必要はありませんが、マッピングしない場合、AutoMapperは浅いコピーを作成します。変更も。

14
Kasey Krehbiel

MemberwiseClone()メソッドはパブリックではないので、オブジェクトを複製しやすくするためにこの単純な拡張メソッドを作成しました。

public static T Clone<T>(this T obj)
{
    var inst = obj.GetType().GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

    return (T)inst?.Invoke(obj, null);
}

使用法:

var clone = myObject.Clone();
13
tocqueville

クラスオブジェクトを複製するには、Object.MemberwiseCloneメソッドを使用できます。

この関数をクラスに追加するだけです:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

深く独立したコピーを実行するには、DeepCopyメソッドを呼び出すだけです:

yourClass newLine = oldLine.DeepCopy();
8
Chtiwi Malek

abは、同じPersonオブジェクトへの2つの参照にすぎません。どちらも本質的にPersonのアドレスを保持しています。

ICloneable インターフェイスがありますが、これをサポートするクラスは比較的少数です。これを使用すると、次のように記述できます。

Person b = a.Clone();

その場合、bは完全に独立したPersonになります。

コピーコンストラクターを実装することもできます。

public Person(Person src)
{
  // ... 
}

すべてのフィールドをコピーする組み込みの方法はありません。リフレクションを介して実行できますが、パフォーマンスが低下します。

6

苦痛なく: NClone libraryを使用する

Person a = new Person() { head = "big", feet = "small" };
Person b = Clone.ObjectGraph(a); 
2
magallanes
  public static T Clone<T>(T obj)
  {
      DataContractSerializer dcSer = new  DataContractSerializer(obj.GetType());
      MemoryStream memoryStream = new MemoryStream();

      dcSer.WriteObject(memoryStream, obj);
      memoryStream.Position = 0;

      T newObject = (T)dcSer.ReadObject(memoryStream);
      return newObject;
  }
1
Tatinfo

MemberwiseCloneは、他の人が示唆しているように、浅いコピーを行う良い方法です。ただし、保護されているため、クラスを変更せずに使用する場合は、リフレクションを介してアクセスする必要があります。しかし、反射は遅いです。したがって、多くのオブジェクトを複製することを計画している場合、結果をキャッシュする価値があるかもしれません。

public static class CloneUtil<T>
{
    private static readonly Func<T, object> clone;

    static CloneUtil()
    {
        var cloneMethod = typeof(T).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        clone = (Func<T, object>)cloneMethod.CreateDelegate(typeof(Func<T, object>));
    }

    public static T ShallowClone(T obj) => (T)clone(obj);
}

public static class CloneUtil
{
    public static T ShallowClone<T>(this T obj) => CloneUtil<T>.ShallowClone(obj);
}

次のように呼び出すことができます。

Person b = a.ShallowClone();
0
Tim Pohlmann

次のようにできます:

var jss = new JavaScriptSerializer();
var b = jss.Deserialize<Person>(jss.Serialize(a));

ディープクローニングについては、次の回答をご覧ください。 https://stackoverflow.com/a/78612/550975

0
Serj Sagan

私の意見では、これを行う最善の方法は、以下に示すように独自のClone()メソッドを実装することです。

class Person
{
    public string head;
    public string feet;

    // Downside: It has to be manually implemented for every class
    public Person Clone()
    {
        return new Person() { head = this.head, feet = this.feet };
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Person a = new Person() { head = "bigAF", feet = "smol" };
        Person b = a.Clone();

        b.head = "notEvenThatBigTBH";

        Console.WriteLine($"{a.head}, {a.feet}");
        Console.WriteLine($"{b.head}, {b.feet}");
    }
}

出力:

bigAf、smol

notEvenThatBigTBH、smol

bは、参照ではなくクローンであるため、aから完全に独立しています。

私が助けてくれることを願っています!

0
Wahoo