Получение KeyNotFoundException при использовании ключа, ранее извлеченного из коллекции ключей? - PullRequest
1 голос
/ 19 января 2010

У меня есть следующий код, где по какой-то причине я получаю исключение KeyNotFoundException, хотя я использую ключ, который я получил несколькими строками выше. Кто-нибудь знает ситуацию, когда это не сработает? Я в тупике. Кстати, SchemaElementType - это перечисление.

public class DefaultValue
{
 private Dictionary<Parameter, string> _params;

 public DefaultValue(Dictionary<Parameter, string> parameters)
 {
        _params = parameters;
 }

  public string GetParameterValue(string name)
  {
      foreach(Parameter param in _params.Keys)
      {
           if(param.ParamName.Equals(name))
           {
               // **** Issue here  ****
               return _params[param];
           }
      }
      return string.Empty;
  }
}

[DataContract]
public class Parameter
    {
        #region Members
        private Guid _guid;
        private Guid _formulaGuid;
        private string _name;

        #endregion

        #region Constructor
        public Parameter(Guid guid, Guid formulaGuid, string name, SchemaElementType type)
        {
            ParamGuid = guid;
            FormulaGuid = formulaGuid;
            ParamName = name;
            ParamType = type;
        }

        public Parameter()
        {}

        #endregion

        #region Properties

        [DataMember]
        public Guid ParamGuid
        {
            get { return _guid; }
            set { _guid = value; }
        }

        [DataMember]
        public Guid FormulaGuid
        {
            get { return _formulaGuid; }
            set { _formulaGuid = value; }
        }

        [DataMember]
        public string ParamName
        {
            get { return _name; }
            set { _name = value; }
        }

        [DataMember]
        public SchemaElementType ParamType { get; set; }

        #endregion

        #region Overrides

        public bool Equals(Parameter other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            bool result =other._guid.Equals(_guid);
            result = result && other._formulaGuid.Equals(_formulaGuid);
            result = result && Equals(other._name, _name);
            result = result && Equals(other.ParamType, ParamType);

            return result;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = _guid.GetHashCode();
                result = (result*397) ^ _formulaGuid.GetHashCode();
                result = (result*397) ^ (_name != null ? _name.GetHashCode() : 0);
                result = (result*397) ^ ParamType.GetHashCode();
                return result;
            }
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Parameter)) return false;
            return Equals((Parameter) obj);
        }

        #endregion
    }

Ответы [ 3 ]

2 голосов
/ 19 января 2010

Я беспокоюсь о том, что Parameter изменчив. Если ( после добавления его в словарь) вы изменили любое из значений, которые используются при генерации GetHashCode() (т.е. все они), тогда все ставки выключены, и вы не гарантированно увидеть ваш товар снова. Я бы не стал делать эти публичные сеттеры, т. Е.

    [DataMember]
    public string ParamName // applies to all the properties, not just this one
    {
        get { return _name; }
        private set { _name = value; }
    }

На самом деле, я бы, вероятно, отбросил явные поля и использовал бы автоматически реализованные свойства C # 3.0:

    [DataMember]
    public string ParamName { get; private set; }

В качестве примера, который ломается при изменении параметра:

    var data = new Dictionary<Parameter, string>();
    Parameter p;
    data.Add((p = new Parameter(Guid.NewGuid(), Guid.NewGuid(), "abc",
        SchemaElementType.A)), "def");
    var dv = new DefaultValue(data);
    string val1 = dv.GetParameterValue("abc"); // returns "def"
    p.ParamGuid = Guid.NewGuid();
    string val2 = dv.GetParameterValue("abc"); // BOOM

В качестве последней мысли; если типичное использование - поиск по string, то почему бы не использовать имя в качестве ключа для внутреннего словаря? В данный момент вы не используете словарь должным образом.

1 голос
/ 19 января 2010

Строго говоря, вы не получаете (ключ поиска) несколькими строками выше, вы получаете объект, который в какой-то момент использовался для вычисления ключа хеша.

Когда вы вставляете в словарь метод GetHashKey ключевого объекта. Если это меняется со времени, когда вы вставляете пару ключ-значение, до времени выполнения вашего кода, вы получите описанное поведение. (кроме случаев, когда GetHashKey no возвращает значение, соответствующее ключу другой паре ключ-значение, в этом случае вы получаете действительно странное поведение, а не исключение)

Я бы посмотрел значение хеш-ключа при вставке и при получении и выяснил, есть ли между ними несоответствие

0 голосов
/ 19 января 2010

Вы можете воспользоваться классом KeyValuePair <>:

foreach(var item in _params)
{
   if(item.Key.ParamName.Equals(name))
   {
      return item.Value;
   }
}
...