オブジェクトグラフの装飾を少し受け入れることができる場合は、protobuf-netを使用できます。(たとえば、nugetを使用して取得できます)
ささいな例:
[Serializable]
[ProtoContract]
public class TestObject
{
[ProtoMember(1)]
public string TestProperty { get; set; }
}
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
Stream stream = new MemoryStream();
using (stream)
{
Serializer.Serialize<T>(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return Serializer.Deserialize<T>(stream);
}
}
}
注:Serializerには実際にはこれに適していると思われるDeepCloneメソッドがありますが、Serializeの後にDeserializeを実行するよりも遅いことがわかりました。
更新:マークの質問に関しては、それは非常に奇妙に思えます。これは私の(非常に限られた)テストであり、ディープクローンを使用すると一貫して約30%遅くなるようです。(注:テストを異なる順序で実行し、並行して実行していない場合でも)
[TestMethod]
public void TestWithStream()
{
var objects = Enumerable.Range(0, 1000000).Select(_ => new TestObject { TestProperty = Guid.NewGuid().ToString() }).ToList();
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < objects.Count; ++i)
{
ObjectCopier.CloneWithStream(objects[i]);
}
Console.WriteLine(w.Elapsed);
}
[TestMethod]
public void TestWithDeepClone()
{
var objects = Enumerable.Range(0, 1000000).Select(_ => new TestObject { TestProperty = Guid.NewGuid().ToString() }).ToList();
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < objects.Count; ++i)
{
ObjectCopier.CloneWithDeepClone(objects[i]);
}
Console.WriteLine(w.Elapsed);
}
public static class ObjectCopier
{
public static T CloneWithStream<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
Stream stream = new MemoryStream();
using (stream)
{
Serializer.Serialize<T>(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return Serializer.Deserialize<T>(stream);
}
}
public static T CloneWithDeepClone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
return Serializer.DeepClone(source);
}
}