Вместо двоичной сериализации вы можете использовать http://code.google.com/p/protobuf-net/, а затем вычислить криптографический хеш.Говорят, что protobuf более компактен, чем Bin Ser (см., например, http://code.google.com/p/protobuf-net/wiki/Performance).
Я добавлю это, учитывая, что вам не нужно сериализоваться.Было бы лучше использовать Reflection и «перемещаться» по объектам, вычисляющим ваш хеш (таким же образом различные сериализаторы «пересекают» ваш объект).См., Например, Использование отражения в C # для получения свойств вложенного объекта
После долгих раздумий и услышав, что сказал @Jon, я могу сказать вам, что моя "вторичная" идея (используя Reflection) ОЧЕНЬ ОЧЕНЬ ОЧЕНЬ сложно, если вы не хотите потратить неделю на написание анализатора объектов.Да, это выполнимо ... Но какое представление вы бы дали данным до вычисления хеша?Чтобы было ясно:
two strings
"A"
"B"
ясно "A", "B"! = "AB", "".Но MD5 («A») в сочетании с MD5 («B») == MD5 («AB») в сочетании с MD5 («»).Вероятно, лучше всего предварительно добавить длину (поэтому, используя нотацию Pascal / BSTR)
И null
значения?Какое «сериализованное» значение они имеют?Еще один вопрос.Ясно, что если вы сериализуете строку как длину + строку (так, чтобы решить предыдущую проблему), вы можете сериализовать ноль просто как "null"
(без длины) ... А объекты?Вы бы добавили идентификатор типа объекта?Это было бы конечно лучше.В противном случае объекты переменной длины могут создать тот же беспорядок, что и строки.
Используя BinaryFormatter (или, возможно, даже protobuf-net), вам не обязательно где-то сохранять сериализованный объект, потому что они оба поддерживают потоковую передачу ...Пример
public class Hasher : Stream
{
protected readonly HashAlgorithm HashAlgorithm;
protected Hasher(HashAlgorithm hash)
{
HashAlgorithm = hash;
}
public static byte[] GetHash(object obj, HashAlgorithm hash)
{
var hasher = new Hasher(hash);
if (obj != null)
{
var bf = new BinaryFormatter();
bf.Serialize(hasher, obj);
}
else
{
hasher.Flush();
}
return hasher.HashAlgorithm.Hash;
}
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
HashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
HashAlgorithm.TransformBlock(buffer, offset, count, buffer, offset);
}
}
static void Main(string[] args)
{
var list = new List<int>(100000000);
for (int i = 0; i < list.Capacity; i++)
{
list.Add(0);
}
Stopwatch sw = Stopwatch.StartNew();
var hash = Hasher.GetHash(list, new MD5CryptoServiceProvider());
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
Я определяю класс Hasher
, который получает сериализацию объекта (фрагмент за раз) и вычисляет хэш в «потоковом режиме».Использование памяти O (1).Время явно O (n) (с n "размер" сериализованного объекта).
Если вы хотите использовать protobuf (но помните, что для сложных объектов необходимо, чтобы они были помечены его атрибутами (или атрибутами WCF или ...))
public static byte[] GetHash<T>(T obj, HashAlgorithm hash)
{
var hasher = new Hasher(hash);
if (obj != null)
{
ProtoBuf.Serializer.Serialize(hasher, obj);
hasher.Flush();
}
else
{
hasher.Flush();
}
return hasher.HashAlgorithm.Hash;
}
единственное «большое» отличие состоит в том, что protobuf не Flush
поток, поэтому мы должны это сделать, и он НАСТОЯЩИМ хочет, чтобы тип корневого объекта был напечатан, а не простой «объект».
Ох ... и на ваш вопрос:
Как мне сериализовать объект?Это должно быть быстро и не потреблять слишком много памяти.Также он всегда должен быть надежно сериализован одинаково.Если я использую сериализацию .NET по умолчанию, могу ли я быть действительно уверен, что созданный двоичный поток всегда одинаков, если точные данные совпадают?Я сомневаюсь в этом.
List<int> l1 = new List<int>();
byte[] bytes1, bytes2;
using (MemoryStream ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, l1);
bytes1 = ms.ToArray();
}
l1.Add(0);
l1.RemoveAt(0);
using (MemoryStream ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, l1);
bytes2 = ms.ToArray();
}
Debug.Assert(bytes1.Length == bytes2.Length);
Скажем так: Debug.Assert
потерпит неудачу.Это потому, что List «сохраняет» некоторый внутренний статус (например, версию).Это очень затрудняет двоичную сериализацию и сравнение.Вам лучше использовать «программируемый» сериализатор (например, proto-buf).Вы говорите ему, какие свойства / поля нужно сериализовать, и он сериализует их.
Так что же может быть альтернативным способом сериализации, который не займет много времени для реализации?
Proto-buf ... или DataContractSerializer (но это довольно медленно).Как вы можете себе представить, в сериализации данных нет серебряной пули.