能否基于Protobuf-Net实现泛型复制构造方法?(避免反射且不新建对象)
Great question! You absolutely can create a generic deep copy method with protobuf-net that populates an existing target object without relying on slow reflection. The secret is leveraging protobuf-net's built-in Merge functionality, which is optimized for exactly this scenario—plus, protobuf-net uses IL generation (not reflection) after the first call to your generic method, so you get excellent performance.
Step 1: Mark Your Class for Protobuf-Net Serialization
First, you need to tell protobuf-net which members of your class to serialize. Add the [ProtoContract] attribute to your class and [ProtoMember] to each property you want copied:
using ProtoBuf; [ProtoContract] public class Person { [ProtoMember(1)] public Int32 Id { get; set; } [ProtoMember(2)] public String FirstName { get; set; } [ProtoMember(3)] public Int32 Age { get; set; } [ProtoMember(4)] public String Name { get; set; } }
Step 2: Implement the Generic Copy Method
Use Serializer.Merge<T> to copy data from the source to the target object. This method handles serialization to an in-memory stream and then merges the data into the existing target—no new object creation required, no reflection overhead after the first call:
using System.IO; using ProtoBuf; public static void DeepCopyConstructor<T>(T source, T target) where T : class { if (source == null || target == null) throw new ArgumentNullException(source == null ? nameof(source) : nameof(target)); using (var stream = new MemoryStream()) { // Serialize the source object to the stream Serializer.Serialize(stream, source); // Reset the stream position to the start stream.Position = 0; // Merge the stream data into the target object Serializer.Merge(stream, target); } }
How This Works (and Why It Avoids Reflection)
- On the first call to
DeepCopyConstructor<T>for a specific typeT, protobuf-net generates optimized IL code for serializing/deserializing that type. This is done once, at runtime. - All subsequent calls reuse this generated IL code—no reflection is involved, making this method far faster than reflection-based copy approaches.
- The
Mergeoperation overwrites the target object's properties with values from the source, effectively performing a deep copy (since protobuf-net handles nested objects recursively).
Example Usage
Test the method with your Person class to verify it works:
// Create a source person with sample data var source = new Person { Id = 1, FirstName = "John", Age = 30, Name = "John Doe" }; // Create an empty target person (or one with existing values you want to overwrite) var target = new Person { Id = 2, FirstName = "Jane" }; // Run the copy method DeepCopyConstructor(source, target); // Now target has all the source's values: // target.Id == 1, target.FirstName == "John", target.Age == 30, target.Name == "John Doe"
Key Notes
- Required Attributes: Don't skip the
[ProtoContract]and[ProtoMember]attributes—protobuf-net needs these to know which members to process. You can customize member numbers (the integer in[ProtoMember(X)]) as needed, just ensure they're unique per class. - Null Handling: The method includes null checks to avoid runtime errors—adjust this if your use case allows nulls.
- Nested Objects: If your class contains other complex objects (e.g., a
List<Address>), just mark those classes with protobuf-net attributes too, and the deep copy will work recursively.
内容的提问来源于stack exchange,提问作者Franki1986

