KeyedCollection<TKey, TItem> Constructor (IEqualityComparer<TKey>^, Int32)
Initializes a new instance of the KeyedCollection<TKey, TItem> class that uses the specified equality comparer and creates a lookup dictionary when the specified threshold is exceeded.
Assembly: mscorlib (in mscorlib.dll)
protected: KeyedCollection( IEqualityComparer<TKey>^ comparer, int dictionaryCreationThreshold )
Parameters
- comparer
-
Type:
System.Collections.Generic::IEqualityComparer<TKey>^
The implementation of the IEqualityComparer<T> generic interface to use when comparing keys, or null to use the default equality comparer for the type of the key, obtained from EqualityComparer<T>::Default.
- dictionaryCreationThreshold
-
Type:
System::Int32
The number of elements the collection can hold without creating a lookup dictionary (0 creates the lookup dictionary when the first item is added), or –1 to specify that a lookup dictionary is never created.
| Exception | Condition |
|---|---|
| ArgumentOutOfRangeException | dictionaryCreationThreshold is less than –1. |
By default, the KeyedCollection<TKey, TItem> includes a lookup dictionary that is created when the first item is added. When an item is added to the KeyedCollection<TKey, TItem>, the item's key is extracted once and saved in the lookup dictionary for faster searches. This constructor allows you to override that behavior. Specify 0 to create the dictionary when the first element is added, 1 to create the dictionary when the second element is added, and so on. If you specify –1 as the threshold, the lookup dictionary is never created.
For very small collections the improvement in retrieval speed provided by the lookup dictionary might not be worth the extra memory required by the dictionary. Setting a threshold allows you to decide when to make that tradeoff.
Note |
|---|
Because the KeyedCollection<TKey, TItem> class is abstract (MustInherit in Visual Basic), you must derive from it in order to use it. In the constructor for your derived type, call the appropriate KeyedCollection<TKey, TItem> constructor. It is not necessary to expose functionality like the equality comparer or the dictionary creation threshold in your constructors. |
This constructor is an O(1) operation.
The following code example shows how to override the protected InsertItem, RemoveItem, ClearItems, and SetItem methods, to provide custom behavior for the Add, Remove, and Clear methods, and for setting the default Item property (the indexer in C#). The custom behavior provided in this example is a notification event named Changed, which is raised at the end of each of the overridden methods.
The code example uses the KeyedCollection<TKey, TItem>(IEqualityComparer<TKey>^, Int32) constructor with a threshold of 0, so that the internal dictionary is created the first time an object is added to the collection.
The code example creates the SimpleOrder class, which derives from KeyedCollection<TKey, TItem> and represents a simple order form. The order form contains OrderItem objects representing items ordered. The code example also creates a SimpleOrderChangedEventArgs class to contain the event information, and an enumeration to identify the type of change.
The code example demonstrates the custom behavior by calling the properties and methods of the derived class, in the Main method of the Demo class.
This code example uses objects with immutable keys. For a code example that uses mutable keys, see ChangeItemKey.
using namespace System; using namespace System::Collections::Generic; using namespace System::Collections::ObjectModel; public enum class ChangeTypes { Added, Removed, Replaced, Cleared }; ref class SimpleOrderChangedEventArgs; // This class represents a simple line item in an order. All the // values are immutable except quantity. // public ref class OrderItem { private: int _quantity; public: initonly int PartNumber; initonly String^ Description; initonly double UnitPrice; OrderItem(int partNumber, String^ description, int quantity, double unitPrice) { this->PartNumber = partNumber; this->Description = description; this->Quantity = quantity; this->UnitPrice = unitPrice; }; property int Quantity { int get() { return _quantity; }; void set(int value) { if (value < 0) throw gcnew ArgumentException("Quantity cannot be negative."); _quantity = value; }; }; virtual String^ ToString() override { return String::Format( "{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}", PartNumber, _quantity, Description, UnitPrice, UnitPrice * _quantity); }; }; // Event argument for the Changed event. // public ref class SimpleOrderChangedEventArgs : EventArgs { public: OrderItem^ ChangedItem; initonly ChangeTypes ChangeType; OrderItem^ ReplacedWith; SimpleOrderChangedEventArgs(ChangeTypes change, OrderItem^ item, OrderItem^ replacement) { this->ChangeType = change; this->ChangedItem = item; this->ReplacedWith = replacement; } }; // This class derives from KeyedCollection and shows how to override // the protected ClearItems, InsertItem, RemoveItem, and SetItem // methods in order to change the behavior of the default Item // property and the Add, Clear, Insert, and Remove methods. The // class implements a Changed event, which is raised by all the // protected methods. // // SimpleOrder is a collection of OrderItem objects, and its key // is the PartNumber field of OrderItem-> PartNumber is an Integer, // so SimpleOrder inherits KeyedCollection<int, OrderItem>. // (Note that the key of OrderItem cannot be changed; if it could // be changed, SimpleOrder would have to override ChangeItemKey.) // public ref class SimpleOrder : KeyedCollection<int, OrderItem^> { public: event EventHandler<SimpleOrderChangedEventArgs^>^ Changed; // 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. // SimpleOrder() : KeyedCollection<int, OrderItem^>(nullptr, 0) {}; // This is the only method that absolutely must be overridden, // because without it the KeyedCollection cannot extract the // keys from the items. // protected: virtual int GetKeyForItem(OrderItem^ item) override { // In this example, the key is the part number. return item->PartNumber; } virtual void InsertItem(int index, OrderItem^ newItem) override { __super::InsertItem(index, newItem); Changed(this, gcnew SimpleOrderChangedEventArgs( ChangeTypes::Added, newItem, nullptr)); } virtual void SetItem(int index, OrderItem^ newItem) override { OrderItem^ replaced = this->Items[index]; __super::SetItem(index, newItem); Changed(this, gcnew SimpleOrderChangedEventArgs( ChangeTypes::Replaced, replaced, newItem)); } virtual void RemoveItem(int index) override { OrderItem^ removedItem = Items[index]; __super::RemoveItem(index); Changed(this, gcnew SimpleOrderChangedEventArgs( ChangeTypes::Removed, removedItem, nullptr)); } virtual void ClearItems() override { __super::ClearItems(); Changed(this, gcnew SimpleOrderChangedEventArgs( ChangeTypes::Cleared, nullptr, nullptr)); } // This method uses the internal reference to the dictionary // to test fo public: void AddOrMerge(OrderItem^ newItem) { int key = this->GetKeyForItem(newItem); OrderItem^ existingItem = nullptr; // The dictionary is not created until the first item is // added, so it is necessary to test for null. Using // AndAlso ensures that TryGetValue is not called if the // dictionary does not exist. // if (this->Dictionary != nullptr && this->Dictionary->TryGetValue(key, existingItem)) { existingItem->Quantity += newItem->Quantity; } else { this->Add(newItem); } } }; public ref class Demo { public: static void Main() { SimpleOrder^ weekly = gcnew SimpleOrder(); weekly->Changed += gcnew EventHandler<SimpleOrderChangedEventArgs^>(ChangedHandler); // The Add method, inherited from Collection, takes OrderItem-> // weekly->Add(gcnew OrderItem(110072674, "Widget", 400, 45.17)); weekly->Add(gcnew OrderItem(110072675, "Sprocket", 27, 5.3)); weekly->Add(gcnew OrderItem(101030411, "Motor", 10, 237.5)); weekly->Add(gcnew OrderItem(110072684, "Gear", 175, 5.17)); Display(weekly); // The Contains method of KeyedCollection takes TKey. // Console::WriteLine("\nContains(101030411): {0}", weekly->Contains(101030411)); // The default Item property of KeyedCollection takes the key // type, Integer. The property is read-only. // Console::WriteLine("\nweekly[101030411]->Description: {0}", weekly[101030411]->Description); // The Remove method of KeyedCollection takes a key. // Console::WriteLine("\nRemove(101030411)"); weekly->Remove(101030411); // The Insert method, inherited from Collection, takes an // index and an OrderItem. // Console::WriteLine("\nInsert(2, gcnew OrderItem(...))"); weekly->Insert(2, gcnew OrderItem(111033401, "Nut", 10, .5)); // The default Item property is overloaded. One overload comes // from KeyedCollection<int, OrderItem>; that overload // is read-only, and takes Integer because it retrieves by key. // The other overload comes from Collection<OrderItem>, the // base class of KeyedCollection<int, OrderItem>; it // retrieves by index, so it also takes an Integer. The compiler // uses the most-derived overload, from KeyedCollection, so the // only way to access SimpleOrder by index is to cast it to // Collection<OrderItem>. Otherwise the index is interpreted // as a key, and KeyNotFoundException is thrown. // Collection<OrderItem^>^ coweekly = weekly; Console::WriteLine("\ncoweekly[2].Description: {0}", coweekly[2]->Description); Console::WriteLine("\ncoweekly[2] = gcnew OrderItem(...)"); coweekly[2] = gcnew OrderItem(127700026, "Crank", 27, 5.98); OrderItem^ temp = coweekly[2]; // The IndexOf method, inherited from Collection<OrderItem>, // takes an OrderItem instead of a key. // Console::WriteLine("\nIndexOf(temp): {0}", weekly->IndexOf(temp)); // The inherited Remove method also takes an OrderItem-> // Console::WriteLine("\nRemove(temp)"); weekly->Remove(temp); Console::WriteLine("\nRemoveAt(0)"); weekly->RemoveAt(0); weekly->AddOrMerge(gcnew OrderItem(110072684, "Gear", 1000, 5.17)); Display(weekly); Console::WriteLine(); weekly->Clear(); } private: static void Display(SimpleOrder^ order) { Console::WriteLine(); for each( OrderItem^ item in order ) { Console::WriteLine(item); } } static void ChangedHandler(Object^ source, SimpleOrderChangedEventArgs^ e) { OrderItem^ item = e->ChangedItem; if (e->ChangeType == ChangeTypes::Replaced) { OrderItem^ replacement = e->ReplacedWith; Console::WriteLine("{0} (quantity {1}) was replaced " + "by {2}, (quantity {3}).", item->Description, item->Quantity, replacement->Description, replacement->Quantity); } else if(e->ChangeType == ChangeTypes::Cleared) { Console::WriteLine("The order list was cleared."); } else { Console::WriteLine("{0} (quantity {1}) was {2}.", item->Description, item->Quantity, e->ChangeType); } } }; void main() { Demo::Main(); } /* This code example produces the following output: Widget (quantity 400) was Added. Sprocket (quantity 27) was Added. Motor (quantity 10) was Added. Gear (quantity 175) was Added. 110072674 400 Widget at 45.17 = 18,068.00 110072675 27 Sprocket at 5.30 = 143.10 101030411 10 Motor at 237.50 = 2,375.00 110072684 175 Gear at 5.17 = 904.75 Contains(101030411): True weekly[101030411]->Description: Motor Remove(101030411) Motor (quantity 10) was Removed. Insert(2, gcnew OrderItem(...)) Nut (quantity 10) was Added. coweekly[2].Description: Nut coweekly[2] = gcnew OrderItem(...) Nut (quantity 10) was replaced by Crank, (quantity 27). IndexOf(temp): 2 Remove(temp) Crank (quantity 27) was Removed. RemoveAt(0) Widget (quantity 400) was Removed. 110072675 27 Sprocket at 5.30 = 143.10 110072684 1175 Gear at 5.17 = 6,074.75 The order list was cleared. */
Available since 8
.NET Framework
Available since 2.0
Portable Class Library
Supported in: portable .NET platforms
Silverlight
Available since 2.0
Windows Phone Silverlight
Available since 7.0
Windows Phone
Available since 8.1
