0

我正在调查以下 NRE:

System.NullReferenceException: Object reference not set to an instance of an object.
at System.Collections.Generic.GenericEqualityComparer`1.GetHashCode(T obj)
at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.ContainsKey(TKey key)
at UC4.Decision.Core.Event.EventObjectHeader.get_Item(String key)
...

我的EventObjectHeader课看起来像这样:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using UC4.Common.Utils.Xml;

namespace UC4.Decision.Core.Event
{
    /// <summary>
    /// Enumeration of the event priority.
    /// </summary>
    public enum EventPriority
    {
        /// <summary>
        /// Event Priority Low
        /// </summary>
        Low = 0,
        /// <summary>
        /// Event Priority Medium (Default)
        /// </summary>
        Medium = 1,
        /// <summary>
        /// Event Priority High
        /// </summary>
        High = 2,
    }

    /// <summary>
    /// Structure for the header information of an event object.
    /// </summary>
    /// <seealso cref="EventObject"/>
    [Serializable]
    public class EventObjectHeader : IEventObjectHeader, ISerializable
    {
        public const string KEY_TYPEURI = "typeUri";

        /// <summary>
        /// Fixed key which is used to store the predefined header element GUID.
        /// </summary>
        public const string KEY_GUID = "guid";

        /// <summary>
        /// Fixed key which is used to store the predefined header element IsPersistent.
        /// </summary>
        public const string KEY_ISPERSISTENT = "isPersistent";

        /// <summary>
        /// Fixed key which is used to store the time created in eventbase timezone of the Event.
        /// </summary>
        public const string KEY_TIME_CREATED = "timeCreated";


        /// <summary>
        /// Fixed key which is used to store the time created in UTC timezone of the Event.
        /// </summary>
        public const string KEY_TIME_CREATED_UTC = "timeCreatedUTC";

        /// <summary>
        /// Fixed key which is used to store the predefined header element Priority.
        /// </summary>
        public const string KEY_PRIORITY = "priority";

        // internal data structure to store header elements as string tuples
        private readonly IDictionary<string, string> _internal = new Dictionary<string, string>(6);

        // lock for read and write requests to the internal dictionary
        private readonly ReaderWriterLockSlim _internalDictionaryLock = new ReaderWriterLockSlim();

        // caching dictionary
        protected readonly Hashtable _cachedHeaderAttributes = Hashtable.Synchronized(new Hashtable());

        /// <summary>
        /// default header attributes (always loaded because they are columns of EB_EVENTS)
        /// </summary>
        public static readonly ICollection<String> DEFAULT_ATTRIBUTES = new List<string>(new[]
                                                                           {
                                                                               KEY_GUID, KEY_TIME_CREATED, 
                                                                               KEY_TIME_CREATED_UTC,KEY_TYPEURI
                                                                           });

        /// <summary>
        /// Initializes a new instance of the <see cref="EventObjectHeader"/> class.
        /// </summary>
        public EventObjectHeader()
        {
        }


        /// <summary>
        /// Indicates whether the event is persistent
        /// </summary>
        public bool IsPersistent
        {
            get
            {
                if (ContainsKey(KEY_ISPERSISTENT) == false)
                {
                    return false;
                }
                return GetValueAs<bool>(KEY_ISPERSISTENT);
            }
            set { SetValue(KEY_ISPERSISTENT, value); }
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="EventObjectHeader"/> class.
        /// </summary>
        /// <param name="eventObjectHeaderToCopy">The event object header to copy.</param>
        private EventObjectHeader(IEventObjectHeader eventObjectHeaderToCopy) : this()
        {
            List<String> keys = new List<string>(eventObjectHeaderToCopy.Keys);
            foreach (string key in keys)
            {
                Add(key, eventObjectHeaderToCopy[key]);
            }
        }

        /// <summary>
        /// Property for accessing the predefined header element GUID.
        /// </summary>
        public virtual Guid Guid
        {
            get
            {
                if (ContainsKey(KEY_GUID) == false)
                {
                    return Guid.Empty;
                }
                return GetValueAs<Guid>(KEY_GUID);
            }
            set { SetValue(KEY_GUID, value); }
        }


        /// <summary>
        /// Property for accessing the predefined header element LocalTimeCreated.
        /// </summary>
        public virtual DateTime TimeCreated
        {
            get
            {
                if (ContainsKey(KEY_TIME_CREATED) == false)
                {
                    return default(DateTime);
                }
                return GetValueAs<DateTime>(KEY_TIME_CREATED);
            }
            set { SetValue(KEY_TIME_CREATED, value); }
        }

        /// <summary>
        /// Property for accessing the predefined header element UTC Time created
        /// </summary>
        public virtual DateTime TimeCreatedUTC
        {
            get
            {
                if (ContainsKey(KEY_TIME_CREATED_UTC) == false)
                {
                    return default(DateTime);
                }
                return GetValueAs<DateTime>(KEY_TIME_CREATED_UTC);
            }
            set { SetValue(KEY_TIME_CREATED_UTC, value); }
        }


        /// <summary>
        /// Property for accessing the predefined header element Priority.
        /// </summary>
        public virtual EventPriority Priority
        {
            get
            {
                if (ContainsKey(KEY_PRIORITY) == false)
                {
                    return EventObjectHeaderHelper.DEFAULT_PRIORITY;
                }
                return GetValueAs<EventPriority>(KEY_PRIORITY);
            }
            set { SetValue(KEY_PRIORITY, value); }
        }

        /// <summary>
        /// Make a copy of the event object header instance.
        /// </summary>
        /// <returns>New instance with the same values</returns>
        public virtual EventObjectHeader Copy()
        {
            return new EventObjectHeader(this);
        }

        /// <summary>
        /// Clear the event object header.
        /// </summary>
        public virtual void Clear()
        {
            _internalDictionaryLock.EnterWriteLock();
            try
            {
                _internal.Clear();
                _cachedHeaderAttributes.Clear();
            }
            finally
            {
                _internalDictionaryLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Access the stored values via the key.
        /// </summary>
        /// <param name="key">Attribute key</param>
        /// <returns>Value</returns>
        public virtual string this[string key]
        {
            set
            {
                _internalDictionaryLock.EnterWriteLock();
                try
                {
                    _internal[key] = value;
                    if (_cachedHeaderAttributes.ContainsKey(key))
                    {
                        _cachedHeaderAttributes.Remove(key);
                    }
                }
                finally
                {
                    _internalDictionaryLock.ExitWriteLock();    
                }
            }
            get
            {
                _internalDictionaryLock.EnterReadLock();
                try
                {
                    if (_internal.ContainsKey(key))
                    {
                        return _internal[key];
                    }    
                }
                finally
                {
                    _internalDictionaryLock.ExitReadLock();
                }
                if (key == KEY_ISPERSISTENT)
                {
                    // this is to prevent a Exception when the IsPersistent attribute
                    // hadn't been set in advance
                    return XmlHelper.XmlConvertValueToString(false);
                }
                throw new EventObjectException(1167, key);
            }
        }

        /// <summary>
        /// Remove a stored value by its key.
        /// If the key was not found, <code>null</code> is returned.
        /// </summary>
        /// <param name="key">Attribute key</param>
        /// <returns>The value of the element which was removed</returns>
        public virtual string Remove(string key)
        {
            string retVal;
            _internalDictionaryLock.EnterWriteLock();
            try
            {
                _internal.TryGetValue(key, out retVal);
                _internal.Remove(key);
                if (_cachedHeaderAttributes.ContainsKey(key))
                {
                    _cachedHeaderAttributes.Remove(key);
                }
            }
            finally
            {
                _internalDictionaryLock.ExitWriteLock();
            }
            return retVal;
        }

        /// <summary>
        /// Get a collection of the stored keys inside the header.
        /// </summary>
        public virtual ICollection<string> Keys
        {
            get
            { 
                _internalDictionaryLock.EnterReadLock();
                try
                {
                     return _internal.Keys;
                }
                finally
                {
                    _internalDictionaryLock.ExitReadLock();
                }                
            }
        }

        /// <summary>
        /// Check if the header contains a specific element.
        /// </summary>
        /// <param name="key">Attribute key</param>
        /// <returns>true, if the header contains a value for this key</returns>
        public virtual bool ContainsKey(string key)
        {
            _internalDictionaryLock.EnterReadLock();
            bool retVal;
            try
            {
                retVal = _internal.ContainsKey(key);    
            }
            finally
            {
                _internalDictionaryLock.ExitReadLock();    
            }
            return retVal;
        }

        /// <summary>
        /// Read only property which counts the elements in the header.
        /// </summary>
        public virtual int Count
        {
            get
            {
                _internalDictionaryLock.EnterReadLock();
                int retVal;
                try
                {
                    retVal = _internal.Count;
                }
                finally
                {
                    _internalDictionaryLock.ExitReadLock();
                }
                return retVal;
            }
        }

        /// <summary>
        /// Get a stored element casted to a specific type.
        /// if the value is not found, the default of this type is returned.
        /// </summary>
        /// <typeparam name="T">Runtime type of the value</typeparam>
        /// <param name="key">Attribute key</param>
        /// <returns>RuntimeType safe value</returns>
        public virtual T GetValueAs<T>(String key)
        {
            try
            {
                if (_cachedHeaderAttributes.ContainsKey(key))
                {
                    Object obj = _cachedHeaderAttributes[key];
                    if (obj is T)
                    {
                        return (T) obj;
                    }
                }
                String value = this[key];
                return XmlHelper.XmlConvertStringToValue<T>(value);
            }
            catch (TargetInvocationException e)
            {
                throw e.InnerException;
            }
        }

        /// <summary>
        /// Add a new element with a specific key.
        /// </summary>
        /// <param name="key">Attribute key</param>
        /// <param name="value">Value</param>
        public virtual void Add(String key, String value)
        {
            Add(key, value, false);
        }

        /// <summary>
        /// Add a new element with a specific key.
        /// </summary>
        /// <typeparam name="T">Optional runtime type parameter</typeparam>
        /// <param name="key">Attribute key</param>
        /// <param name="value">Value</param>
        public virtual void Add<T>(String key, T value)
        {
            Add(key, value, false);
        }

        /// <summary>
        /// Set a value for a specific key.
        /// If the key was already used, the value is overwritten and the old one is returned.
        /// </summary>
        /// <typeparam name="T">Optional runtime type parameter</typeparam>
        /// <param name="key">Attribute key</param>
        /// <param name="value">Value</param>
        /// <returns>The old value if it exists</returns>
        public virtual string SetValue<T>(String key, T value)
        {
            string retVal = null;
            _internalDictionaryLock.EnterWriteLock();
            try
            {
                if (_internal.ContainsKey(key))
                {
                    retVal = _internal[key];
                }    
                _internal.Remove(key);
                _internal.Add(key, XmlHelper.XmlConvertValueToString(value));
                _cachedHeaderAttributes[key] = value;
            }
            catch (TargetInvocationException e)
            {
                throw e.InnerException;
            }
            finally
            {
                _internalDictionaryLock.ExitWriteLock();
            }
            return retVal;
        }


        // generic add value to the header
        protected virtual void Add<T>(String key, T value, bool overwrite)
        {
            _internalDictionaryLock.EnterWriteLock();
            try
            {
                if (overwrite)
                {
                    _internal.Remove(key);
                }
                _internal.Add(key, XmlHelper.XmlConvertValueToString(value));
                _cachedHeaderAttributes[key] = value;
            }
            catch (TargetInvocationException e)
            {
                throw e.InnerException;
            }
            finally
            {
                _internalDictionaryLock.ExitWriteLock();
            }
        }

        #region ISerializable Members

        /// <summary>
        /// Create a new instance from the serialization stream.
        /// </summary>
        /// <param name="info">Serializer information</param>
        /// <param name="context">Serialization stream context</param>
        protected EventObjectHeader(SerializationInfo info, StreamingContext context)
        {
            _internal =
                info.GetValue("headerAttributes", typeof (Dictionary<string, string>)) as Dictionary<string, string>;
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("headerAttributes", _internal);
        }

        #endregion
    }
}

对字典的所有访问_internal都受保护ReaderWriterLockSlim- 因此我看不到任何可能的并发问题。

使用 dotPeek,我深入Dictionary课堂。它的ContainsKey方法只是返回this.FindEntry(key) >= 0FindEntry方法如下所示:

private int FindEntry(TKey key)
{
  if ((object) key == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
  if (this.buckets != null)
  {
    int num = this.comparer.GetHashCode(key) & int.MaxValue;
    for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
    {
      if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
        return index;
    }
  }
  return -1;
}

GenericEqualityComparer'sGetHashCode方法看起来像这样:

public override int GetHashCode(T obj)
{
  if ((object) obj == null)
    return 0;
  else
    return obj.GetHashCode();
}

最后,我所做的只是一个简单ContainsKeyDictionary<string, string>. 我对 NRE 没有任何解释,但它似乎经常在我们客户的一个生产系统中发生。什么可以解释 NullReferenceException?

4

0 回答 0