Deep Copy using Reflection in an Extension Method for Silverlight? - c#

So I'm trying to find a generic extension method that creates a deep copy of an object using reflection, that would work in Silverlight. Deep copy using serialization is not so great in Silverlight, since it runs in partial trust and the BinaryFormatter does not exist. I also know that reflection would be faster then serialization for cloning.
It would be nice to have a method that works to copy public, private and protected fields, and is recursive so that it can copy objects in objects, and that would also be able to handle collections, arrays, etc.
I have searched online, and can only find shallow copy implementations using reflection. I don't understand why, since you can just use MemberwiseClone, so to me, those implementations are useless.
Thank You.

For data contract objects we have used the following helper method for deep cloning within Silverlight:
public static T Clone<T>(T source)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, source);
ms.Seek(0, SeekOrigin.Begin);
return (T)serializer.ReadObject(ms);
}
}
Used like this:
var clone = CloneHelper.Clone<MyDTOType>(dtoVar);

Required Namespaces:
using System.Reflection;
using System.Collections.Generic;
Method:
private readonly static object _lock = new object();
public static T cloneObject<T>(T original, List<string> propertyExcludeList)
{
try
{
Monitor.Enter(_lock);
T copy = Activator.CreateInstance<T>();
PropertyInfo[] piList = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo pi in piList)
{
if (!propertyExcludeList.Contains(pi.Name))
{
if (pi.GetValue(copy, null) != pi.GetValue(original, null))
{
pi.SetValue(copy, pi.GetValue(original, null), null);
}
}
}
return copy;
}
finally
{
Monitor.Exit(_lock);
}
}
This is not specific to Silverlight in any way - it is just plain Reflection.
As written it will only work with objects that have a parameterless constructor. To use objects that require constructor parameters, you will need to pass in an object[] with the parameters, and use a different overload of the Activator.CreateInstance method e.g.
T copy = (T)Activator.CreateInstance(typeof(T), initializationParameters);
The propertyExcludeList parameter is a list of property names that you wish to exclude from the copy, if you want to copy all properties just pass an empty list e.g.
new List<string>()

Can't you just use regular .NET reflection? Serialize your object to a MemoryStream and then deserialize it back. This will create a deep copy (ultimately using reflection) and will require hardly any code on your part:
T DeepCopy<T>(T instance)
{
BinaryFormatter formatter=new BinaryFormatter();
using(var stream=new MemoryStream())
{
formatter.Serialize(stream, instance);
stream.Position=0;
return (T)formatter.Deserialize(stream);
}
}

Related

How to find object position in the serialized generic list

For I can have direct access to a particular object in a serialized generic list, I need to know position of it from de-serialized stream.
private static void Serialze(object obj, FileStream Stream)
{
BinaryFormatter bin = new BinaryFormatter();
bin.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low;
bin.Serialize(Stream, obj);
}
private static object DeSerialize(FileStream Stream)
{
if (Stream.Position >= Stream.Length) return null;
BinaryFormatter bin = new BinaryFormatter();
bin.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low;
object x = bin.Deserialize(Stream);
return x;
}
Suppose I have a generic list like this:
List<MyClass> L1=new List<MyClass>();
How can I to find position of L1[10] in my FileStreem, for using it, in DeSerialize method?
The format that BinaryFormatter uses is not seekable. You cannot extract sub-objects.
Probably, this question is an instance of the XY-Problem. Tell us what you want to achieve and why you need this. As asked, there is no answer.
Maybe you can make use of a database instead. Or, use Protocol Buffers, which are more flexible.

The input stream is not a valid binary format. The starting contents

I've seen this type of question asked before but not sure what the root cause of the problem was or how to fix it.
I am modifying an existing class to be able to load data into the member variables from flash. Right now, the class loads data from a file through the load function. This function has been overloaded to take in byte array.
The data read back from the flash is put into this byte array.
The error that is thrown is (happens at the line ... = formatter.Deserialize(stream)):
The input stream is not a valid binary format. The starting contents (in bytes) are: 93-E3-E6-3F-C3-F5-E4-41-00-C0-8D-C3-14-EE-4A-C3-00 ...
The interesting thing here is that the contents are exactly the contents of the byte array that is being passed into the stream. In other words, this is the data from the flash and this is exactly what I want serialized. I'm not sure why the error is being thrown.
Or a better question is what is a is a valid binary format for a BinaryFormatter? Does it need a certain size? Is there specific end value needed? Are certain values invalid? The current size of the byte array input is 24 bytes.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;
using System.IO;
using Galileo.Data;
using System.Xml.Serialization;
using System.Reflection;
using System.Runtime.InteropServices;
using UComm;
using System.Runtime.Serialization.Formatters.Binary;
using ULog;
public void Load(byte[] spCalfromPrimary)
{
try
{
Type settingsType = this.GetType();
object tmp = Activator.CreateInstance(settingsType);
Stream stream = new MemoryStream();
stream.Write(spCalfromPrimary, 0, spCalfromPrimary.Length);
stream.Position = 0;
BinaryFormatter formatter = new BinaryFormatter();
//tmp = formatter.Deserialize(stream);
formatter.Deserialize(stream); //**<--- throws error here**
// Use reflection to copy all public properties from the temporary object into this one.
PropertyInfo[] properties = settingsType.GetProperties();
foreach (PropertyInfo property in properties)
{
object value = property.GetValue(tmp, null);
if (value == null)
{
throw new FileFormatException("Null value encountered in settings file");
}
else
{
property.SetValue(this, value, null);
}
}
}
catch (Exception ex)
{
_logger.DebugException("Failed to load spatial cal value from FW", ex);
Console.WriteLine(ex.Message);
}
}
// <summary>
/// Loads the setting from file
/// </summary>
public void Load()
{
Type settingsType = this.GetType();
XmlSerializer xser = new XmlSerializer(settingsType);
object tmp = Activator.CreateInstance(settingsType);
using (StreamReader reader = new StreamReader(_filename)) { tmp = xser.Deserialize(reader); }
// Use reflection to copy all public properties from the temporary object into this one.
PropertyInfo[] properties = settingsType.GetProperties();
foreach (PropertyInfo property in properties)
{
object value = property.GetValue(tmp, null);
if (value == null)
{
throw new FileFormatException("Null value encountered in settings file");
}
else
{
property.SetValue(this, value, null);
}
}
}
Note that I have also tried the a Convert byte array to object function (I found on stackoverflow). When I used this function, an exception was still thrown at .Deserialize(memStream).
// Convert a byte array to an Object
private Object ByteArrayToObject(byte[] arrBytes)
{
MemoryStream memStream = new MemoryStream();
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
Object obj = (Object) binForm.Deserialize(memStream);
return obj;
}
Apparently I left out some important information.
Serialization happens in a different application from deserialization. Serialization uses a bitconverter to take the data, convert to a byte array and upload it to flash. Let me explain. The data that is being serialized / deserialized & stored in flash is calibration data. Calibration is performed at the factory with Application1 by production. This uses a bitconverter to put every field into a stream and then serialize the stream.
CFlatInterface.FloatToStream(bData, ref i, rtsMappingData.ScaleTrackingDMD);
CFlatInterface.FloatToStream(bData, ref i, rtsMappingData.RotationAngle);
CFlatInterface.FloatToStream(bData, ref i, rtsMappingData.CenterOfRotation.dx);
where the function FloatToStream is defined as:
public static void FloatToStream(byte[] buf, ref int index, float val)
{
Buffer.BlockCopy(BitConverter.GetBytes(val), 0, buf, index, sizeof(float));
index += sizeof(float);
}
So every field that makes up the Calibration is put into the stream this way. The data is put into the stream and a byte array is constructed and sent to flash.
On the other side, once the product is out of the factory and in use, Application2 (user application) has a Calibration object that has all the calibration fields. This reads the flash, and gets the data that was written by Application1. Application2 is trying to deserialize the calibration data using BinaryFormatter and the code above. I am coming to the conclusion this is not possible (Thanks Rotem). The correct course of action is to use the same formatter for both serialization / deserialization - i will implement it this way and indicate if that makes a difference.
Following your update, the obvious issue is that you are serializing and deserializing with different formatters.
BinaryFormatter serializes more than just field data. It also serializes type information and metadata so it knows how to deserialize the objects, so it is expecting more than just raw data bytes as input.
Either use a BinaryFormatter on the serializing end as well, or use a manual deserialization technique on the receiving end.

How do you configure protobuf-net's RuntimeModel.Default to support serializing/deserializing a SessionSecurityToken?

BinaryFormatter is able to handle serialization simply:
private byte[] TokenToBytes(SessionSecurityToken token)
{
if (token == null)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, token);
return memoryStream.ToArray();
}
}
When I tried replacing BinaryFormatter with protobuf-net:
using (var memoryStream = new MemoryStream())
{
Serializer.Serialize(memoryStream, token);
return memoryStream.ToArray();
}
I get the following exception:
Type is not expected, and no contract can be inferred:
System.IdentityModel.Tokens.SessionSecurityToken
I tried adding:
RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), true);
Which gets past the exception but I now get a zero byte array.
How do I properly configure protobuf-net to serialize a SessionSecurityToken?
On the flipside, SessionSecurityToken does not have a parameterless constructor.
using (var memoryStream = new MemoryStream(tokenAsBytes))
{
return Serializer.Deserialize<SessionSecurityToken>(memoryStream);
}
Throws a ProtoException:
No parameterless constructor found for SessionSecurityToken
BinaryFormatter is able to do it without any fuss:
using (var memoryStream = new MemoryStream(bytes))
{
var binaryFormatter = new BinaryFormatter();
return (SessionSecurityToken)binaryFormatter.Deserialize(memoryStream);
}
How do I properly configure protobuf-net to deserialize a SessionSecurityToken?
protobuf-net does not claim to be able to serialize every single type; indeed, you would have great difficulty serializing that via most serializers (XmlSerializer, any of the json serializers, DataContractSerializer, etc). BinaryFormatter is in a different category of serializers - and in this particular case, implements custom serialization via ISerializable.GetObjectData(SerializationInfo, StreamingContext).
The constructor thing is a red herring; actually, protobuf-net can bypass constructors completely, and in this particular scenario BinaryFormatter is using a custom serialization constructor via .ctor(SerializationInfo, StreamingContext).
For simple cases, protobuf-net can be configured via attributes or runtime options; for more complex scenarios, surrogates can be used to map between representations - however, in this case I would suggest (looking at the implementation of SessionSecurityToken) that this is more complex than you probably want to maintain.
I would step back a step or two here; most serializers are designed to work with data, not implementation - and work great with DTOs etc. SessionSecurityToken is very much not a DTO, and there is no simple way of switching between them. My strong suggestion here would be: serialize what this represents, not what it is. However, if this is part of an existing complex model and is really really hard to separate out, you could switch back to BinaryFormatter for those bits. I haven't tested this, but consider:
RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), false)
.SetSurrogate(typeof(BinaryFormatterSurrogate<SessionSecurityToken>));
With:
[ProtoContract]
public class BinaryFormatterSurrogate<T>
{
[ProtoMember(1)]
public byte[] Raw { get; set; }
public static explicit operator T(BinaryFormatterSurrogate<T> value)
{
if(value==null || value.Raw == null) return default(T);
using(var ms = new MemoryStream(value.Raw))
{
return (T)new BinaryFormatter().Deserialize(ms);
}
}
public static explicit operator BinaryFormatterSurrogate<T>(T value)
{
object obj = value;
if (obj == null) return null;
using (var ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, obj);
return new BinaryFormatterSurrogate<T> { Raw = ms.ToArray() };
}
}
}
Keep in mind that this simply embeds the output of one serializer as raw data inside another. Fortunately protobuf-net is happy talking binary, so this won't add any noticeable overhead (just the header and length-prefix for the blob) - but it also won't do anything particularly smart or clever with the SessionSecurityToken instances. If this is the only thing you are serializing, it really isn't worth it. If this is just one ugly bump in a larger DTO model, where most of it can serialize nicely - then it might get the job done for you.

How do I clone an ancestor in c#?

I need a semi-shallow copy of an object. Under my original design I used MemberwiseClone to catch all the simple stuff and then I specifically copied the classes to the extent that they needed to be copied. (Some of them are inherently static and most of the rest are containers holding static items.) I didn't like the long list of copies but there's no way around that.
Now, however, I find myself needing to create a descendent object--do I now have to go back and copy all those fields that previously I was copying with MemberwiseClone?
Or am I missing some better workaround for this?
The easiest way to clone, I find, is to use serialization. This obviously only works with classes that are [Serializable] or that implement ISerializable.
Here is a general generic extension you can use to make any serializable class' objects cloneable:
public static T Clone<T>(this T source)
{
if (source == default(T))
{
return default(T);
} else {
IFormatter formatter = new BinaryFormatter();
Stream ms = new MemoryStream();
using (ms)
{
formatter.Serialize(ms, source);
stream.Seek(0, SeekOrigin.Begin);
return (T) formatter.Deserialize(ms);
}
}
}

C# What is the best way to copy a BindingList?

What is the best way to copy a BindingList?
Just use ForEach()? Or are there better ways?
BindingList has a constructor which can take an IList. And BindingList implements IList. So you can just do the following:
BindingList newBL = new BindingList(oldBL);
Of course that creates a second list that just points at the same objects. If you actually want to clone the objects in the list then you have to do more work.
Foreach pretty much is the easiest way, and the performance overhead is minimal if any.
From a deleted answer:
Serialize the object then de-serialize
to get a deep cloned non referenced
copy
Which is a valid option if the OP wants a deep copy.
We use the Serialize / De-serialize route to get a deep copy of the list. It works well but it does slow performance down in larger lists, such as for search screens, so I'd avoid using it on lists with 5000+ items.
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace ProjectName.LibraryName.Namespace
{
internal static class ObjectCloner
{
///
/// Clones an object by using the .
///
/// The object to clone.
///
/// The object to be cloned must be serializable.
///
public static object Clone(object obj)
{
using (MemoryStream buffer = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(buffer, obj);
buffer.Position = 0;
object temp = formatter.Deserialize(buffer);
return temp;
}
}
}
}

Categories

Resources