Từ điển khóa tổng hợp


90

Tôi có một số đối tượng trong List, giả sử List<MyClass>và MyClass có một số thuộc tính. Tôi muốn tạo một chỉ mục của danh sách dựa trên 3 thuộc tính của MyClass. Trong trường hợp này, 2 thuộc tính là int và một thuộc tính là datetime.

Về cơ bản, tôi muốn có thể làm điều gì đó như:

Dictionary< CompositeKey , MyClass > MyClassListIndex = Dictionary< CompositeKey , MyClass >();
//Populate dictionary with items from the List<MyClass> MyClassList
MyClass aMyClass = Dicitonary[(keyTripletHere)];

Đôi khi tôi tạo nhiều từ điển trên một danh sách để lập chỉ mục các thuộc tính khác nhau của các lớp mà nó chứa. Tôi không chắc chắn cách tốt nhất để xử lý các phím tổng hợp. Tôi đã xem xét thực hiện tổng kiểm tra của ba giá trị nhưng điều này có nguy cơ xảy ra va chạm.


2
Tại sao bạn không sử dụng Tuples? Họ làm tất cả việc tổng hợp cho bạn.
Eldritch Conundrum

21
Tôi không biết làm thế nào để trả lời điều đó. Bạn hỏi câu hỏi đó như thể bạn đã cho rằng tôi đang cố tình né tránh các bộ giá trị.
AaronLS

6
Xin lỗi, tôi đã viết lại nó như một câu trả lời chi tiết hơn.
Eldritch Conundrum

1
Trước khi triển khai một lớp tùy chỉnh, hãy đọc về Tuple (theo đề xuất của Eldritch Conundrum) - msdn.microsoft.com/en-us/library/system.tuple.aspx . Chúng dễ thay đổi hơn và sẽ giúp bạn tiết kiệm việc tạo các lớp tùy chỉnh.
OSH

Câu trả lời:


105

Bạn nên sử dụng bộ giá trị. Chúng tương đương với một lớp CompositeKey, nhưng Equals () và GetHashCode () đã được triển khai cho bạn.

var myClassIndex = new Dictionary<Tuple<int, bool, string>, MyClass>();
//Populate dictionary with items from the List<MyClass> MyClassList
foreach (var myObj in myClassList)
    myClassIndex.Add(Tuple.Create(myObj.MyInt, myObj.MyBool, myObj.MyString), myObj);
MyClass myObj = myClassIndex[Tuple.Create(4, true, "t")];

Hoặc sử dụng System.Linq

var myClassIndex = myClassList.ToDictionary(myObj => Tuple.Create(myObj.MyInt, myObj.MyBool, myObj.MyString));
MyClass myObj = myClassIndex[Tuple.Create(4, true, "t")];

Trừ khi bạn cần tùy chỉnh tính toán của hàm băm, còn không thì việc sử dụng các bộ giá trị sẽ đơn giản hơn.

Nếu có nhiều thuộc tính bạn muốn đưa vào khóa tổng hợp, tên kiểu Tuple có thể trở nên khá dài, nhưng bạn có thể làm cho tên ngắn hơn bằng cách tạo lớp của riêng bạn bắt nguồn từ Tuple <...>.


** được chỉnh sửa vào năm 2017 **

Có một tùy chọn mới bắt đầu bằng C # 7: bộ giá trị . Ý tưởng giống nhau, nhưng cú pháp khác, nhẹ hơn:

Kiểu Tuple<int, bool, string>trở thành (int, bool, string), và giá trị Tuple.Create(4, true, "t")trở thành (4, true, "t").

Với các bộ giá trị, bạn cũng có thể đặt tên cho các phần tử. Lưu ý rằng hiệu suất hơi khác nhau, vì vậy bạn có thể muốn thực hiện một số điểm chuẩn nếu chúng quan trọng đối với bạn.


4
Tuple không phải là một ứng cử viên sáng giá cho một khóa vì nó tạo ra một số lượng lớn các xung đột băm. stackoverflow.com/questions/12657348/…
paparazzo

1
@Blam KeyValuePair<K,V>và các cấu trúc khác có hàm băm mặc định được cho là không tốt (xem stackoverflow.com/questions/3841602/… để biết thêm chi tiết). Tuple<>tuy nhiên không phải là một ValueType, và hàm băm mặc định của nó ít nhất sẽ sử dụng tất cả các trường. Điều đó đang được nói, nếu vấn đề chính của mã của bạn là xung đột, thì hãy triển khai một tối ưu hóa GetHashCode()phù hợp với dữ liệu của bạn.
Eldritch Conundrum,

1
Mặc dù tuple không phải là một ValueType từ thử nghiệm của tôi nó bị aa rất nhiều va chạm
tay săn ảnh

5
Tôi nghĩ rằng câu trả lời này đã lỗi thời khi chúng ta có ValueTuples. Họ có cú pháp đẹp hơn trong C #, và họ dường như làm GetHashCode hai lần nhanh như Tuples - gist.github.com/ljw1004/61bc96700d0b03c17cf83dbb51437a69
Lucian Wischik

3
@LucianWischik Cảm ơn bạn, tôi đã cập nhật câu trả lời để đề cập đến chúng.
Eldritch Conundrum

22

Cách tốt nhất tôi có thể nghĩ đến là tạo cấu trúc CompositeKey và đảm bảo ghi đè các phương thức GetHashCode () và Equals () để đảm bảo tốc độ và độ chính xác khi làm việc với bộ sưu tập:

class Program
{
    static void Main(string[] args)
    {
        DateTime firstTimestamp = DateTime.Now;
        DateTime secondTimestamp = firstTimestamp.AddDays(1);

        /* begin composite key dictionary populate */
        Dictionary<CompositeKey, string> compositeKeyDictionary = new Dictionary<CompositeKey, string>();

        CompositeKey compositeKey1 = new CompositeKey();
        compositeKey1.Int1 = 11;
        compositeKey1.Int2 = 304;
        compositeKey1.DateTime = firstTimestamp;

        compositeKeyDictionary[compositeKey1] = "FirstObject";

        CompositeKey compositeKey2 = new CompositeKey();
        compositeKey2.Int1 = 12;
        compositeKey2.Int2 = 9852;
        compositeKey2.DateTime = secondTimestamp;

        compositeKeyDictionary[compositeKey2] = "SecondObject";
        /* end composite key dictionary populate */

        /* begin composite key dictionary lookup */
        CompositeKey compositeKeyLookup1 = new CompositeKey();
        compositeKeyLookup1.Int1 = 11;
        compositeKeyLookup1.Int2 = 304;
        compositeKeyLookup1.DateTime = firstTimestamp;

        Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup1]);

        CompositeKey compositeKeyLookup2 = new CompositeKey();
        compositeKeyLookup2.Int1 = 12;
        compositeKeyLookup2.Int2 = 9852;
        compositeKeyLookup2.DateTime = secondTimestamp;

        Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup2]);
        /* end composite key dictionary lookup */
    }

    struct CompositeKey
    {
        public int Int1 { get; set; }
        public int Int2 { get; set; }
        public DateTime DateTime { get; set; }

        public override int GetHashCode()
        {
            return Int1.GetHashCode() ^ Int2.GetHashCode() ^ DateTime.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj is CompositeKey)
            {
                CompositeKey compositeKey = (CompositeKey)obj;

                return ((this.Int1 == compositeKey.Int1) &&
                        (this.Int2 == compositeKey.Int2) &&
                        (this.DateTime == compositeKey.DateTime));
            }

            return false;
        }
    }
}

Một bài viết MSDN trên GetHashCode ():

http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx


Tôi không nghĩ rằng đó thực sự chắc chắn 100% là một mã băm duy nhất, rất có thể.
Hans Olsson

Điều đó rất có thể đúng! Theo bài báo MSDN được liên kết, đó là cách được khuyến nghị để ghi đè GetHashCode (). Tuy nhiên, vì tôi không sử dụng nhiều phím tổng hợp trong công việc hàng ngày của mình nên tôi không thể nói chắc chắn.
Allen E. Scharfenberg

4
Đúng. Nếu bạn tháo rời Dictionary.FindEntry () bằng Reflector, bạn sẽ thấy rằng cả mã băm VÀ bình đẳng đầy đủ đều được kiểm tra. Trước tiên, mã băm được kiểm tra và nếu nó không thành công, điều kiện ngắn mạch mà không kiểm tra sự bình đẳng đầy đủ. Nếu băm vượt qua, sự bình đẳng cũng được kiểm tra.
Jason Kleban

1
Và vâng, dấu bằng cũng nên được ghi đè để khớp. Ngay cả khi bạn thực hiện GetHashCode () trả về 0 cho bất kỳ trường hợp nào, thì Từ điển vẫn hoạt động, nó sẽ chậm hơn.
Jason Kleban

2
Kiểu Tuple nội trang triển khai tổ hợp băm là '(h1 << 5) + h1 ^ h2' thay vì 'h1 ^ h2' của bạn. Tôi đoán họ làm điều đó để tránh va chạm mỗi khi hai đối tượng được băm bằng cùng một giá trị.
Eldritch Conundrum,

13

Làm thế nào về Dictionary<int, Dictionary<int, Dictionary<DateTime, MyClass>>>?

Điều này sẽ cho phép bạn làm:

MyClass item = MyData[8][23923][date];

1
điều này sẽ tạo ra nhiều đối tượng hơn sau đó tạo ra một cấu trúc hoặc lớp CompositeKey. và cũng sẽ chậm hơn vì hai cấp độ tra cứu sẽ được sử dụng.
Ian Ringrose

Tôi tin rằng đó là cùng một số lượng so sánh - Tôi không hiểu làm thế nào sẽ có nhiều đối tượng hơn - cách tổng hợp khóa vẫn cần một khóa và đó là các giá trị thành phần hoặc đối tượng và một lệnh để giữ chúng. Cách lồng nhau này, bạn không cần khóa trình bao bọc cho mỗi đối tượng / giá trị, một lệnh bổ sung cho mỗi cấp tổ bổ sung. Bạn nghĩ sao?
Jason Kleban

9
Dựa trên điểm chuẩn của tôi, tôi đã thử với các khóa có 2 và 3 phần: giải pháp từ điển lồng nhau nhanh hơn 3-4 lần so với cách sử dụng phương pháp khóa tổng hợp tuple. Tuy nhiên, cách tiếp cận tuple dễ dàng / gọn gàng hơn rất nhiều.
RickL

5
@RickL Tôi có thể xác nhận những điểm chuẩn đó, chúng tôi sử dụng một loại trong cơ sở mã của chúng tôi, được gọi là CompositeDictionary<TKey1, TKey2, TValue>(v.v.) đơn giản là kế thừa từ Dictionary<TKey1, Dictionary<TKey2, TValue>>(hoặc tuy nhiên, cần có nhiều từ điển lồng nhau. Mà không cần tự triển khai toàn bộ loại từ đầu (thay vì gian lận bằng cách sử dụng từ điển lồng nhau hoặc các loại để chứa các phím) này là nhanh nhất chúng tôi nhận được.
Adam Houldsworth

1
Phương pháp tiếp cận dict lồng nhau sẽ chỉ nhanh hơn đối với một nửa (?) Các trường hợp không có dữ liệu, vì các từ điển trung gian có thể bỏ qua tính toán và so sánh mã băm đầy đủ. Khi có dữ liệu, nó sẽ chậm hơn vì các hoạt động cơ bản như Thêm, Chứa, v.v. phải được thực hiện ba lần. Tôi chắc chắn rằng lợi nhuận với phương pháp tiếp cận tuple bị đánh bại trong một số tiêu chuẩn được đề cập ở trên là về chi tiết triển khai của các bộ giá trị .NET khá kém nếu xét đến hình phạt quyền anh mà nó mang lại cho các loại giá trị. Một bộ ba thực hiện đúng là những gì tôi sẽ đi với, coi bộ nhớ quá
nawfal

12

Bạn có thể lưu trữ chúng trong một cấu trúc và sử dụng nó làm khóa:

struct CompositeKey
{
  public int value1;
  public int value2;
  public DateTime value3;
}

Liên kết để nhận mã băm: http://msdn.microsoft.com/en-us/library/system.valuetype.gethashcode.aspx


Tôi bị mắc kẹt trên .NET 3.5 nên tôi không có quyền truy cập vào Tuples vì vậy đây là một giải pháp tốt!
aarona

Tôi ngạc nhiên vì điều này không được ủng hộ nhiều hơn. Đó là một giải pháp đơn giản dễ đọc hơn Tuple.
Đánh dấu

1
Theo msdn, điều này thực hiện tốt, nếu không có trường nào là kiểu tham chiếu, nếu không nó sử dụng phản xạ để bình đẳng.
Gregor Slavec

@Mark Vấn đề với cấu trúc là việc triển khai GetHashCode () mặc định của nó thực sự không đảm bảo sử dụng tất cả các trường của cấu trúc (dẫn đến hiệu suất từ ​​điển kém), trong khi Tuple cung cấp bảo đảm như vậy. Tôi đã thử nghiệm nó. Xem stackoverflow.com/questions/3841602/… để biết chi tiết đẫm máu.
Eldritch Conundrum

8

Bây giờ VS2017 / C # 7 đã ra mắt, câu trả lời tốt nhất là sử dụng ValueTuple:

// declare:
Dictionary<(string, string, int), MyClass> index;

// populate:
foreach (var m in myClassList) {
  index[(m.Name, m.Path, m.JobId)] = m;
}

// retrieve:
var aMyClass = index[("foo", "bar", 15)];

Tôi đã chọn khai báo từ điển với một ValueTuple ẩn danh (string, string, int). Nhưng tôi có thể đặt tên cho họ (string name, string path, int id).

Thông thường, ValueTuple mới nhanh hơn Tuple tại GetHashCodenhưng chậm hơn Equals. Tôi nghĩ bạn cần phải thực hiện các thử nghiệm đầu cuối hoàn chỉnh để tìm ra cách nào thực sự nhanh nhất cho kịch bản của bạn. Nhưng sự đẹp đẽ từ đầu đến cuối và cú pháp ngôn ngữ cho ValueTuple khiến nó trở nên thành công.

// Perf from https://gist.github.com/ljw1004/61bc96700d0b03c17cf83dbb51437a69
//
//              Tuple ValueTuple KeyValuePair
//  Allocation:  160   100        110
//    Argument:   75    80         80    
//      Return:   75   210        210
//        Load:  160   170        320
// GetHashCode:  820   420       2700
//      Equals:  280   470       6800

Vâng, tôi đã trải qua một cuộc viết lại lớn chỉ để giải pháp Loại ẩn danh bị thổi bay vào mặt tôi (không thể so sánh các loại ẩn danh được tạo với các tập hợp khác nhau). ValueTuple dường như là một giải pháp tương đối tốt cho vấn đề về khóa từ điển ghép.
Hoàn toàn

5

Hai cách tiếp cận ngay lập tức xuất hiện trong tâm trí:

  1. Hãy làm theo đề xuất của Kevin và viết một cấu trúc sẽ đóng vai trò là chìa khóa của bạn. Đảm bảo làm cho cấu trúc này triển khai IEquatable<TKey>và ghi đè lên nó EqualsGetHashCodecác phương thức *.

  2. Viết một lớp sử dụng các từ điển lồng nhau trong nội bộ. Một cái gì đó như: TripleKeyDictionary<TKey1, TKey2, TKey3, TValue>... lớp này sẽ có trong nội thành viên của loại Dictionary<TKey1, Dictionary<TKey2, Dictionary<TKey3, TValue>>>, và sẽ tiếp xúc với các phương pháp như this[TKey1 k1, TKey2 k2, TKey3 k3], ContainsKeys(TKey1 k1, TKey2 k2, TKey3 k3)vv

* Một lời giải thích về việc liệu việc ghi đè Equalsphương thức có cần thiết hay không: mặc dù đúng là Equalsphương thức cho một cấu trúc so sánh giá trị của từng thành viên theo mặc định, nhưng nó làm như vậy bằng cách sử dụng phản ánh - vốn dĩ đòi hỏi chi phí hiệu suất - và do đó không phải là rất triển khai phù hợp cho một cái gì đó được sử dụng làm khóa trong từ điển (dù sao theo ý kiến ​​của tôi). Theo tài liệu MSDN về ValueType.Equals:

Việc triển khai mặc định của phương thức Equals sử dụng sự phản chiếu để so sánh các trường tương ứng của obj và instance này. Ghi đè phương thức Equals cho một kiểu cụ thể để cải thiện hiệu suất của phương thức và thể hiện chặt chẽ hơn khái niệm bình đẳng cho kiểu.


Về vấn đề 1, tôi không nghĩ bạn cần ghi đè Equals và GetHashcode, việc triển khai Equals mặc định sẽ tự động kiểm tra sự bình đẳng trên tất cả các trường mà tôi nghĩ là ổn trên cấu trúc này.
Hans Olsson

@ho: Có thể không cần thiết , nhưng tôi thực sự khuyên bạn nên làm như vậy đối với bất kỳ cấu trúc nào sẽ đóng vai trò là khóa. Xem bản chỉnh sửa của tôi.
Dan Tao

3

Nếu khóa là một phần của lớp thì hãy sử dụng KeyedCollection.
Nó là Dictionarynơi mà khóa có nguồn gốc từ đối tượng.
Dưới bìa là Từ điển
Không phải lặp lại phím trong dấu KeyValue.
Tại sao lại có cơ hội, chìa khóa không giống Keyvới Value.
Không cần phải sao chép cùng một thông tin trong bộ nhớ.

Lớp KeyedCollection

Trình lập chỉ mục để hiển thị khóa tổng hợp

    using System.Collections.ObjectModel;

    namespace IntIntKeyedCollection
    {
        class Program
        {
            static void Main(string[] args)
            {
                Int32Int32DateO iid1 = new Int32Int32DateO(0, 1, new DateTime(2007, 6, 1, 8, 30, 52));
                Int32Int32DateO iid2 = new Int32Int32DateO(0, 1, new DateTime(2007, 6, 1, 8, 30, 52));
                if (iid1 == iid2) Console.WriteLine("same");
                if (iid1.Equals(iid2)) Console.WriteLine("equals");
                // that are equal but not the same I don't override = so I have both features

                Int32Int32DateCollection int32Int32DateCollection = new Int32Int32DateCollection();
                // dont't have to repeat the key like Dictionary
                int32Int32DateCollection.Add(new Int32Int32DateO(0, 0, new DateTime(2008, 5, 1, 8, 30, 52)));
                int32Int32DateCollection.Add(new Int32Int32DateO(0, 1, new DateTime(2008, 6, 1, 8, 30, 52)));
                int32Int32DateCollection.Add(iid1);
                //this would thow a duplicate key error
                //int32Int32DateCollection.Add(iid2);
                //this would thow a duplicate key error
                //int32Int32DateCollection.Add(new Int32Int32DateO(0, 1, new DateTime(2008, 6, 1, 8, 30, 52)));
                Console.WriteLine("count");
                Console.WriteLine(int32Int32DateCollection.Count.ToString());
                // reference by ordinal postion (note the is not the long key)
                Console.WriteLine("oridinal");
                Console.WriteLine(int32Int32DateCollection[0].GetHashCode().ToString());
                // reference by index
                Console.WriteLine("index");
                Console.WriteLine(int32Int32DateCollection[0, 1, new DateTime(2008, 6, 1, 8, 30, 52)].GetHashCode().ToString());
                Console.WriteLine("foreach");
                foreach (Int32Int32DateO iio in int32Int32DateCollection)
                {
                    Console.WriteLine(string.Format("HashCode {0} Int1 {1} Int2 {2} DateTime {3}", iio.GetHashCode(), iio.Int1, iio.Int2, iio.Date1));
                }
                Console.WriteLine("sorted by date");
                foreach (Int32Int32DateO iio in int32Int32DateCollection.OrderBy(x => x.Date1).ThenBy(x => x.Int1).ThenBy(x => x.Int2))
                {
                    Console.WriteLine(string.Format("HashCode {0} Int1 {1} Int2 {2} DateTime {3}", iio.GetHashCode(), iio.Int1, iio.Int2, iio.Date1));
                }
                Console.ReadLine();
            }
            public class Int32Int32DateCollection : KeyedCollection<Int32Int32DateS, Int32Int32DateO>
            {
                // This parameterless constructor calls the base class constructor 
                // that specifies a dictionary threshold of 0, so that the internal 
                // dictionary is created as soon as an item is added to the  
                // collection. 
                // 
                public Int32Int32DateCollection() : base(null, 0) { }

                // This is the only method that absolutely must be overridden, 
                // because without it the KeyedCollection cannot extract the 
                // keys from the items.  
                // 
                protected override Int32Int32DateS GetKeyForItem(Int32Int32DateO item)
                {
                    // In this example, the key is the part number. 
                    return item.Int32Int32Date;
                }

                //  indexer 
                public Int32Int32DateO this[Int32 Int1, Int32 Int2, DateTime Date1]
                {
                    get { return this[new Int32Int32DateS(Int1, Int2, Date1)]; }
                }
            }

            public struct Int32Int32DateS
            {   // required as KeyCollection Key must be a single item
                // but you don't really need to interact with Int32Int32DateS directly
                public readonly Int32 Int1, Int2;
                public readonly DateTime Date1;
                public Int32Int32DateS(Int32 int1, Int32 int2, DateTime date1)
                { this.Int1 = int1; this.Int2 = int2; this.Date1 = date1; }
            }
            public class Int32Int32DateO : Object
            {
                // implement other properties
                public Int32Int32DateS Int32Int32Date { get; private set; }
                public Int32 Int1 { get { return Int32Int32Date.Int1; } }
                public Int32 Int2 { get { return Int32Int32Date.Int2; } }
                public DateTime Date1 { get { return Int32Int32Date.Date1; } }

                public override bool Equals(Object obj)
                {
                    //Check for null and compare run-time types.
                    if (obj == null || !(obj is Int32Int32DateO)) return false;
                    Int32Int32DateO item = (Int32Int32DateO)obj;
                    return (this.Int32Int32Date.Int1 == item.Int32Int32Date.Int1 &&
                            this.Int32Int32Date.Int2 == item.Int32Int32Date.Int2 &&
                            this.Int32Int32Date.Date1 == item.Int32Int32Date.Date1);
                }
                public override int GetHashCode()
                {
                    return (((Int64)Int32Int32Date.Int1 << 32) + Int32Int32Date.Int2).GetHashCode() ^ Int32Int32Date.GetHashCode();
                }
                public Int32Int32DateO(Int32 Int1, Int32 Int2, DateTime Date1)
                {
                    Int32Int32DateS int32Int32Date = new Int32Int32DateS(Int1, Int2, Date1);
                    this.Int32Int32Date = int32Int32Date;
                }
            }
        }
    }

Đối với việc sử dụng loại giá trị fpr, khóa mà Microsoft đặc biệt khuyến cáo không nên sử dụng.

ValueType.GetHashCode

Tuple Về mặt kỹ thuật không phải là một loại giá trị nhưng có cùng một triệu chứng (xung đột băm) và không phải là ứng cử viên tốt cho một khóa.


+1 để có câu trả lời đúng hơn. Ngạc nhiên không ai đề cập đến nó sớm hơn. Trong thực tế, tùy thuộc vào ý định của OP để sử dụng cấu trúc như thế nào, HashSet<T>với một thích hợp IEqualityComparer<T>cũng sẽ là một lựa chọn. Btw, tôi nghĩ rằng câu trả lời của bạn sẽ thu hút lên phiếu nếu bạn có thể thay đổi tên của bạn lớp và tên thành viên khác :)
nawfal

2

Tôi có thể đề xuất một giải pháp thay thế - một đối tượng ẩn danh. Nó giống như chúng tôi sử dụng trong phương pháp GroupBy LINQ với nhiều khóa.

var dictionary = new Dictionary<object, string> ();
dictionary[new { a = 1, b = 2 }] = "value";

Nó có vẻ lạ, nhưng tôi đã đánh giá chuẩn Tuple.GetHashCode và các phương thức {a = 1, b = 2} .GetHashCode mới và các đối tượng ẩn danh chiến thắng trên máy của tôi trên .NET 4.5.1:

Đối tượng - 89,1732 ms cho 10000 cuộc gọi trong 1000 chu kỳ

Tuple - 738,4475 ms cho 10000 cuộc gọi trong 1000 chu kỳ


omg, sự thay thế này chưa bao giờ có trong tâm trí tôi ... Tôi không biết liệu nó có hoạt động tốt không nếu bạn sử dụng một khóa phức hợp làm khóa tổng hợp.
Gabriel Espinoza

Nếu bạn chỉ truyền một đối tượng (thay vì ẩn danh) thì kết quả của phương thức GetHashCode của đối tượng này sẽ được sử dụng. Nếu bạn sử dụng nó như vậy dictionary[new { a = my_obj, b = 2 }]thì mã băm kết quả sẽ là sự kết hợp của my_obj.GetHashCode và ((Int32) 2) .GetHashCode.
Michael Logutov

KHÔNG SỬ DỤNG PHƯƠNG PHÁP NÀY! Các tập hợp khác nhau tạo ra các tên khác nhau cho các loại ẩn danh. Mặc dù nó có vẻ ẩn danh đối với bạn, nhưng đằng sau hậu trường, có một lớp cụ thể được tạo và hai đối tượng của hai lớp khác nhau sẽ không bằng với toán tử mặc định.
Quarkly

Và điều đó quan trọng như thế nào trong trường hợp này?
Michael Logutov

0

Một giải pháp khác cho những cái đã được đề cập sẽ là lưu trữ một số loại danh sách tất cả các khóa được tạo cho đến nay và khi một đối tượng mới được tạo, bạn tạo mã băm của nó (chỉ như một điểm bắt đầu), hãy kiểm tra xem nó đã có trong danh sách chưa, nếu nó là, sau đó thêm một số giá trị ngẫu nhiên, v.v. vào nó cho đến khi bạn có một khóa duy nhất, sau đó lưu trữ khóa đó trong chính đối tượng và trong danh sách và trả lại đó làm khóa mọi lúc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.