2016-12-08 1 views
0

У меня есть хэш много вещей ... и я сохраняю хэш как своего рода контент-идентичность. Я использую эти вещи повсюду. Хеши - это 20-байтовые массивы, и я недавно сменил их на (по-видимому) простой unsafe struct в проекте C#, который имеет метод ToString(). Однако во время выполнения визуализация всегда значение по умолчанию (все ноль) - даже после изменения содержимого.Почему визуализатор VS 2015 не работает над этой небезопасной структурой?

Единственные данные экземпляра в структуре - это фиксированный массив байтов, который записывается несколькими способами. Без метода ToString() визуализатор показал некоторое слабое представление значения - но это был (я думаю) адрес фиксированного массива.

Ни один из методов перестановки не приводит к тому, что визуализатор изменяется от визуализации по умолчанию.

Например:

visualization

Даже если метод ToString() производит следующие действия:

visualization2

... что ожидаемое значение (и ожидаемой визуализации).

Я пробовал [DebuggerDisplay("{ToString()}")] и делал это сериализуемым, но все же получал те же результаты. Итак, значит ли это, что мне не повезло с небезопасными структурами, или я делаю что-то неправильно, что я не определил?

Edit:

Мои извинения за то, не вкладывая в полной, поддающейся проверке образца. Вот полное консольное приложение, которое демонстрирует проблему. Просто замените содержимое Class1.cs на этот код, и вы увидите проблему.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Security.Cryptography; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
    static void Main(string[ ] args) 
    { 
     // sample data... 
     var bytes = new byte[ 20 ] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; 

     var hash1 = Hash.FromBytes(bytes); //deserailize a hash from array 
     Debug.WriteLine(hash1); 

     var hash2 = new Hash(bytes, 0, 20); //computes a hash over array 
     Debug.WriteLine(hash2); 

     using (var stream = new MemoryStream(bytes)) 
     { 
     var hash3 = new Hash();// empty hash 
     hash3.Read(stream); // deserialize a hash from stream 
     Debug.WriteLine(hash3); 

     stream.Position = 0; 

     var hash4 = new Hash(stream); //compute hash over stream 
     Debug.WriteLine(hash4); 

     var hash5 = new Hash("Compute the hash of a string"); 
     Debug.WriteLine(hash5); 

     Debug.Assert(hash1 == hash3, "Oops!"); 
     Debug.Assert(hash2 == hash4, "Nope!"); 
     Debug.Assert(hash1 != hash2, "Golly!"); 
     Debug.Assert(hash3 != hash4, "Shucks!"); 

     } 
    } 
    } 


    /// <summary>Represents a hash of a string or byte array</summary> 
    [StructLayout(LayoutKind.Sequential)] 
    public unsafe struct Hash: IComparable<Hash> 
    { 

    #region statics and constants 
    /// <summary>Character map for byte array to string</summary> 
    readonly static char[ ] hex = new char[ ] { 
       '0', '1', '2', '3', 
       '4', '5', '6', '7', 
       '8', '9', 'a', 'b', 
       'c', 'd', 'e', 'f' }; 

    /// <summary>Synchronization primitive</summary> 
    readonly static object sync = new object(); 

    /// <summary>Buffer for reading hashes from streams, strings, and arrays</summary> 
    readonly static byte[ ] buffer = new byte[ 20 ]; 

    /// <summary>ToString workspace</summary> 
    static char[ ] hexChars = new char[ Length * 2 ]; 

    /// <summary>Returns a hash that has no value</summary> 
    public readonly static Hash EmptyHash = new Hash(); 

    /// <summary>Retruns the length of any <see cref="Hash"/></summary> 
    public const int Length = 20; 


    /// <summary>Returns a <see cref="HashAlgorithm"/> that the system uses to compute hashes</summary> 
    public static HashAlgorithm GetHasher() 
    { 
     return new SHA1Managed(); 
    } 

    #endregion 

    #region private data 
    /// <summary>A pointer to the underlying data</summary> 
    fixed byte value[ 20 ]; 
    #endregion 

    #region construction 

    /// <summary>Creates a hash from a string</summary> 
    public Hash(string hashable) 
    { 
     fixed (byte* bytes = value, sourceBytes = GetHasher().ComputeHash(Encoding.Unicode.GetBytes(hashable))) 
     { 
     NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
     } 
    } 

    /// <summary>Creates a hash from a byte array</summary> 
    public Hash(byte[ ] source, int index, int length) 
    { 
     fixed (byte* bytes = value, sourceBytes = GetHasher().ComputeHash(source, index, length)) 
     { 
     NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
     } 
    } 

    /// <summary>Creates a hash from a series of hashes</summary> 
    public Hash(IEnumerable<Hash> hashes) 
    { 
     var hasher = GetHasher(); 
     var buffer = new byte[ Length ]; 
     hashes.Do(key => 
     { 
     key.CopyTo(buffer); 
     hasher.TransformBlock(buffer, 0, Length, buffer, 0); 
     }); 
     hasher.TransformFinalBlock(buffer, 0, 0); 
     fixed (byte* bytes = value, source = hasher.Hash) 
     { 
     NativeMethods.CopyMemory(bytes, source, Length); 
     } 
    } 


    /// <summary>Creates a hash over a stream from current position to end</summary> 
    public Hash(Stream stream) 
    { 
     const int bufferSize = 4096; 
     var hasher = GetHasher(); 
     var bytesRead = 0; 
     var buffer = new byte[ bufferSize ]; 
     while (true) 
     { 
     bytesRead = stream.Read(buffer, 0, bufferSize); 
     if (bytesRead == 0) 
     { 
      hasher.TransformFinalBlock(buffer, 0, 0); 
      break; 
     } 
     else 
     { 
      hasher.TransformBlock(buffer, 0, bytesRead, buffer, 0); 
     } 
     } 
     fixed (byte* bytes = value, source = hasher.Hash) 
     { 
     NativeMethods.CopyMemory(bytes, source, Length); 
     } 
    } 

    #endregion 


    #region methods 
    /// <summary>Copies the hash to the start of a byte array</summary> 
    public void CopyTo(byte[ ] buffer) 
    { 
     CopyTo(buffer, 0); 
    } 

    /// <summary>Copies the hash to a byte array</summary> 
    public void CopyTo(byte[ ] buffer, int offset) 
    { 
     if (buffer == null) throw new ArgumentNullException(nameof(buffer)); 
     if (buffer.Length < (offset + Length)) throw new ArgumentOutOfRangeException(nameof(buffer)); 
     fixed (byte* bytes = value, dest = buffer) 
     { 
     NativeMethods.CopyMemory(dest + offset, bytes, Length); 
     } 
    } 

    /// <summary>Returns a byte-array representation of the <see cref="Hash"/></summary> 
    /// <remarks>The returned value is a copy</remarks> 
    public byte[ ] GetBytes() 
    { 
     var results = new byte[ Length ]; 
     fixed (byte* bytes = value, target = results) 
     { 
     NativeMethods.CopyMemory(target, bytes, Length); 
     } 
     return results; 
    } 

    /// <summary>Compares this hash to another</summary> 
    public int CompareTo(Hash other) 
    { 
     var comparedByte = 0; 
     fixed (byte* bytes = value) 
     { 
     for (int i = 0; i < Length; i++) 
     { 
      comparedByte = (*(bytes + i)).CompareTo(other.value[ i ]); 
      if (comparedByte != 0) break; 
     } 
     return comparedByte; 
     } 
    } 

    /// <summary>Returns true if <paramref name="obj"/> is a <see cref="Hash"/> and it's value exactly matches</summary> 
    /// <param name="obj">The <see cref="Hash"/> to compare to this one</param> 
    /// <returns>true if the values match</returns> 
    public override bool Equals(object obj) 
    { 
     if (obj == null || !(obj is Hash)) return false; 
     var other = (Hash) obj; 
     return CompareTo(other) == 0; 
    } 

    /// <summary>Returns a .Net hash code for this <see cref="Hash"/></summary> 
    public override int GetHashCode() 
    { 
     unchecked 
     { 
     int hashCode = 17; 
     fixed (byte* bytes = value) 
     { 
      for (int i = 0; i < Length; i++) 
      { 
      hashCode = hashCode * 31 + *(bytes + i); 
      } 
      return hashCode; 
     } 
     } 
    } 

    /// <summary>Returns a hex string representation of the hash</summary> 
    public override string ToString() 
    { 
     lock (sync) 
     { 
     fixed (char* hexFixed = hex, hexCharsFixed = hexChars) 
     { 
      fixed (byte* bytes = value) 
      { 
      for (int i = 0; i < Length; i++) 
      { 
       *(hexCharsFixed + (i * 2)) = *(hexFixed + (*(bytes + i) >> 4)); 
       *(hexCharsFixed + (1 + (i * 2))) = *(hexFixed + (*(bytes + i) & 0xf)); 
      } 
      return new string(hexChars); 
      } 
     } 
     } 
    } 

    /// <summary>Reads a <see cref="Hash"/> from the provided stream</summary> 
    public void Read(Stream stream) 
    { 
     lock (sync) 
     { 
     var retryCount = 0; 
     var bytesRead = ReadStream(stream, buffer, 0, Length, ref retryCount); 
     if (bytesRead == Length) 
     { 
      fixed (byte* bytes = value, sourceBytes = buffer) 
      { 
      NativeMethods.CopyMemory(bytes, sourceBytes, Length); 
      } 
     } 
     } 
    } 

    /// <summary>Tries hard to populate a <see cref="Hash"/> from a stream - across multiple reads if necessary - up to a point</summary> 
    int ReadStream(Stream stream, byte[ ] buffer, int offset, int length, ref int retryCount) 
    { 
     const int maxStreamReadRetries = 3; 

     var bytesRead = stream.Read(buffer, offset, length); 
     var done = bytesRead == 0 || bytesRead == length; // eos, timeout, or success 
     if (!done) 
     { 
     if (retryCount++ >= maxStreamReadRetries) return 0; 
     bytesRead += ReadStream(stream, buffer, bytesRead, length - bytesRead, ref retryCount); 
     } 
     return bytesRead; 
    } 

    /// <summary>Writes the hash to a stream</summary> 
    public void Write(Stream stream) 
    { 
     lock (sync) 
     { 
     fixed (byte* bytes = value, targetBytes = buffer) 
     { 
      NativeMethods.CopyMemory(targetBytes, bytes, Length); 
     } 
     stream.Write(buffer, 0, Length); 
     } 
    } 

    /// <summary>Returns true if the hash has no value</summary> 
    public bool IsEmpty() 
    { 
     return Equals(EmptyHash); 
    } 

    /// <summary>Returns the result of XORing two <see cref="Hash"/>es</summary> 
    public static Hash Combine(Hash a, Hash b) 
    { 
     var results = new Hash(); 
     for (int i = 0; i < Length; i++) 
     { 
     *(results.value + i) = (byte) (*(a.value + i)^*(b.value + i)); 
     } 
     return results; 
    } 

    /// <summary>Returns the first non-empty hash from a list</summary> 
    public static Hash FirstNotEmpty(params Hash[ ] hashes) 
    { 
     foreach (var hash in hashes) if (!hash.IsEmpty()) return hash; 
     throw new ArgumentOutOfRangeException(nameof(hashes)); 
    } 

    /// <summary>Implements == operator</summary> 
    public static bool operator ==(Hash a, Hash b) 
    { 
     return a.Equals(b); 
    } 

    /// <summary>Implements != operator</summary> 
    public static bool operator !=(Hash a, Hash b) 
    { 
     return !a.Equals(b); 
    } 


    /// <summary>Converts a byte array to a <see cref="Hash"/></summary> 
    public static Hash FromBytes(byte[ ] hashBytes, int offset = 0) 
    { 
     if (hashBytes == null) throw new ArgumentNullException(nameof(hashBytes)); 
     if ((hashBytes.Length + offset) < Length) throw new ArgumentOutOfRangeException(nameof(hashBytes)); 
     var hash = new Hash(); 
     fixed (byte* sourceBytes = hashBytes) 
     NativeMethods.CopyMemory(hash.value, sourceBytes + offset, Length); 
     return hash; 
    } 
    #endregion 

    } 

    class NativeMethods 
    { 
    [DllImport("Kernel32", SetLastError = true, EntryPoint = "CopyMemory")] 
    internal unsafe static extern void CopyMemory(void* destination, void* source, uint length); 
    } 


    static class Extensions 
    { 
    /// <summary>Applies action to each element of the collection.</summary> 
    public static void Do<T>(this IEnumerable<T> enumerable, Action<T> action) 
    { 
     if (enumerable == null) throw new ArgumentNullException("enumerable"); 
     if (action == null) throw new ArgumentNullException("action"); 
     foreach (var item in enumerable) action(item); 
    } 
    } 
} 

Установите контрольную точку ближе к концу основного метода, и пусть ваш курсор парить над любой из hash1 через hash5 переменных после того, как они были созданы.

Примечание: Вы должны установить позволяют небезопасный код в свойствах проекта.

+0

Возможно связанные - http://stackoverflow.com/questions/34138112/a-pointer-type-static-fields-value-is-displayed-as-zero-0x0-by-the-debugger -whi – stuartd

+0

Согласен - было бы здорово, если бы был ответ ;-) Я видел его и все равно писал, что было достаточно различий, и ни один из комментариев в этом вопросе не принес мне ничего полезного. – Clay

+0

@ Клей, можете ли вы поделиться полным образцом, используя один диск? Поэтому я мог бы отлаживать его на моей стороне, используя тот же образец. –

ответ

2

В разделе Инструменты-> Параметры-> Отладка-> Общие-> Включить "Использовать управляемый режим совместимости". Это будет хорошо.

enter image description here

+0

Большое спасибо! Очень приятно видеть эти ценности. – Clay

+0

+1, решает и мою проблему. Вы можете задать тот же ответ здесь: [link] (http://stackoverflow.com/questions/34138112/a-pointer-type-static-fields-value-is-displayed-as-zero-0x0-by-the -debugger-бея) –

Смежные вопросы