5

ベースアイテムのリスト(正確ImmutableHashSet<ListItem>には System.Collections.Immutable から)があり、次のコードを呼び出そうとします

_baseList.Contains(derivedItem)

しかし、これはfalseを返します。

次のコード行はすべてtrueを返しますが、

object.ReferenceEquals(_baseList.First(), derivedItem)
object.Equals(_baseList.First(), derivedItem)
_baseList.First().GetHashCode() == derivedItem.GetHashCode()

次のように書くこともできますが、true が返されます。

_baseList.OfType<DerivedClass>().Contains(derivedItem)

私は何を間違っていますか.OfTypeのものを書くことは避けたいです。

編集:

private ImmutableHashSet<BaseClass> _baseList;

public class BaseClass
{

}

public class DerivedClass : BaseClass
{

}

public void DoStuff()
{
    var items = _baseList.OfType<DerivedClass>().ToList();
    foreach (var derivedItem in items)
    {
        RemoveItem(derivedItem);
    }
}

public void RemoveItem(BaseClass derivedItem)
{
    if (_baseList.Contains(derivedItem))
    {
        //doesn't reach this place, since _baseList.Contains(derivedItem) returns false...
        _baseList = _baseList.Remove(derivedItem);
    }

    //object.ReferenceEquals(_baseList.First(), derivedItem) == true
    //object.Equals(_baseList.First(), derivedItem) == true
    //_baseList.First().GetHashCode() == derivedItem.GetHashCode() == true
    //_baseList.OfType<DerivedClass>().Contains(derivedItem) == true
}

編集2:

ImmutableHashSet<>ここで私の問題の再現可能なコードは、キャッシュのように見えGetHashCode、現在とリスト内のエントリを比較しません。アイテムの数が異なる可能性があることをGetHashCode伝える方法はありますか?そのいまいましい同じ参照...ImmutableHashSet<>GetHashCode

namespace ConsoleApplication1
{
    class Program
    {
        private static ImmutableHashSet<BaseClass> _baseList;

        static void Main(string[] args)
        {
            _baseList = ImmutableHashSet.Create<BaseClass>();
            _baseList = _baseList.Add(new DerivedClass("B1"));
            _baseList = _baseList.Add(new DerivedClass("B2"));
            _baseList = _baseList.Add(new DerivedClass("B3"));
            _baseList = _baseList.Add(new DerivedClass("B4"));
            _baseList = _baseList.Add(new DerivedClass("B5"));

            DoStuff();
            Console.WriteLine(_baseList.Count); //output is 5 - put it should be 0...
            Console.ReadLine();
        }

        private static void DoStuff()
        {
            var items = _baseList.OfType<DerivedClass>().ToList();
            foreach (var derivedItem in items)
            {
                derivedItem.BaseString += "Change...";
                RemoveItem(derivedItem);
            }
        }

        private static void RemoveItem(BaseClass derivedItem)
        {
            if (_baseList.Contains(derivedItem))
            {
                _baseList = _baseList.Remove(derivedItem);
            }
        }
    }

    public abstract class BaseClass
    {
        private string _baseString;
        public string BaseString
        {
            get { return _baseString; }
            set { _baseString = value; }
        }

        public BaseClass(string baseString)
        {
            _baseString = baseString;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hashCode = (_baseString != null ? _baseString.GetHashCode() : 0);
                return hashCode;
            }
        }
    }
    public class DerivedClass : BaseClass
    {
        public DerivedClass(string baseString)
            : base(baseString)
        {

        }
    }
}

ImmutableHashSet<>をコードに変更するとImmutableList<>問題なく動作するので、皆さんが良いアイデアを思いつかない場合は、リストに切り替えます。

4

2 に答える 2

4

あなたの編集であなた自身の質問に答えたと思います。項目を HashSet に追加した後は、hashCode を変更することはできません。これは、HashSet がどのように機能するかという契約を破ります。

このトピックの詳細については、Eric Lippert によるこの優れた記事を参照してください。

特に、次のように述べています。

ガイドライン: GetHashCode によって返される整数は決して変更しないでください

理想的には、変更可能なオブジェクトのハッシュ コードは、変更できないフィールドのみから計算する必要があります。したがって、オブジェクトのハッシュ値は、その存続期間全体にわたって同じです。

ただし、これは理想的な状況のガイドラインにすぎません。実際のルールは次のとおりです。

規則: GetHashCode によって返される整数は、オブジェクトがハッシュ コードが安定していることに依存するデータ構造に含まれている間、決して変更してはなりません。

危険ではありますが、オブジェクトのフィールドが変化するにつれてハッシュ コード値が変化する可能性があるオブジェクトを作成することは許容されます。そのようなオブジェクトがあり、それをハッシュテーブルに入れる場合、オブジェクトを変更するコードとハッシュテーブルを維持するコードには、オブジェクトが存在している間に変更されないことを保証する合意されたプロトコルが必要です。ハッシュテーブル。そのプロトコルがどのように見えるかはあなた次第です。

オブジェクトのハッシュ コードがハッシュ テーブル内にある間に変化する可能性がある場合、明らかに、Contains メソッドは機能しなくなります。オブジェクトをバケット #5 に入れ、それを変更します。セットに変更されたオブジェクトが含まれているかどうかを尋ねると、セットはバケット #74 を探しますが、見つかりません。

オブジェクトは、予期しない方法でハッシュ テーブルに配置される可能性があることを覚えておいてください。多くの LINQ シーケンス演算子は、ハッシュ テーブルを内部的に使用します。オブジェクトを返す LINQ クエリを列挙しているときに、危険なほどオブジェクトを変更しないでください。

編集:ところで、あなたの投稿とその後の編集は、無関係な情報であると感じるものを除外しようとするのではなく、最初から問題の完全で再現可能な作業コードを常に投稿する必要がある理由の完璧な例です。1 時間前にあなたの投稿を見たほとんどの人は、最初からすべての関連情報を持っていれば、一瞬で正しい答えを返すことができたはずです。

于 2015-06-04T14:41:16.523 に答える
4

辞書やその他のハッシュ関連のデータ構造で使用されるオブジェクトは、不変の ID を持つ必要があります。すべてのハッシュ関連のデータ構造は、オブジェクトを辞書に追加すると、そのハッシュコードが変更されないことを前提としています。

このコードは動作しません:

    private static void DoStuff()
    {
        var items = _baseList.OfType<DerivedClass>().ToList();
        foreach (var derivedItem in items)
        {
            derivedItem.BaseString += "Change...";
            RemoveItem(derivedItem);
        }
    }

    private static void RemoveItem(BaseClass derivedItem)
    {
        if (_baseList.Contains(derivedItem))
        {
            _baseList = _baseList.Remove(derivedItem);
        }
    }

_baseList.Contains()RemoveItem()によって呼び出されるinは、格納されたアイテムの ID (そのプロパティDoStuff()) を変更したため、すべてのアイテムに対して false を返します。BaseString

于 2015-06-04T14:37:12.477 に答える