This documentation is archived and is not being maintained.

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.

Namespace:  System.Collections.ObjectModel
Assembly:  mscorlib (in mscorlib.dll)

protected KeyedCollection(
	IEqualityComparer<TKey> comparer,
	int dictionaryCreationThreshold


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.
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.


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.


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 System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

// 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 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.
    public SimpleOrder() : 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 int GetKeyForItem(OrderItem item)
        // In this example, the key is the part number.
        return item.PartNumber;

    protected override void InsertItem(int index, OrderItem newItem)
        base.InsertItem(index, newItem);

        EventHandler<SimpleOrderChangedEventArgs> temp = Changed;
        if (temp != null)
            temp(this, new SimpleOrderChangedEventArgs(
                ChangeType.Added, newItem, null));

    protected override void SetItem(int index, OrderItem newItem)
        OrderItem replaced = Items[index];
        base.SetItem(index, newItem);

        EventHandler<SimpleOrderChangedEventArgs> temp = Changed;
        if (temp != null)
            temp(this, new SimpleOrderChangedEventArgs(
                ChangeType.Replaced, replaced, newItem));

    protected override void RemoveItem(int index)
        OrderItem removedItem = Items[index];

        EventHandler<SimpleOrderChangedEventArgs> temp = Changed;
        if (temp != null)
            temp(this, new SimpleOrderChangedEventArgs(
                ChangeType.Removed, removedItem, null));

    protected override void ClearItems()

        EventHandler<SimpleOrderChangedEventArgs> temp = Changed;
        if (temp != null)
            temp(this, new SimpleOrderChangedEventArgs(
                ChangeType.Cleared, null, null));

// Event argument for the Changed event.
public class SimpleOrderChangedEventArgs : EventArgs
    private OrderItem _changedItem;
    private ChangeType _changeType;
    private OrderItem _replacedWith;

    public OrderItem ChangedItem { get { return _changedItem; }}
    public ChangeType ChangeType { get { return _changeType; }}
    public OrderItem ReplacedWith { get { return _replacedWith; }}

    public SimpleOrderChangedEventArgs(ChangeType change, 
        OrderItem item, OrderItem replacement)
        _changeType = change;
        _changedItem = item;
        _replacedWith = replacement;

public enum ChangeType

public class Demo
    public static void Main()
        SimpleOrder weekly = new SimpleOrder();
        weekly.Changed += new 

        // The Add method, inherited from Collection, takes OrderItem.
        weekly.Add(new OrderItem(110072674, "Widget", 400, 45.17));
        weekly.Add(new OrderItem(110072675, "Sprocket", 27, 5.3));
        weekly.Add(new OrderItem(101030411, "Motor", 10, 237.5));
        weekly.Add(new OrderItem(110072684, "Gear", 175, 5.17));


        // The Contains method of KeyedCollection takes TKey.
        Console.WriteLine("\nContains(101030411): {0}", 

        // The default Item property of KeyedCollection takes the key
        // type, Integer. The property is read-only.
        Console.WriteLine("\nweekly[101030411].Description: {0}", 

        // The Remove method of KeyedCollection takes a key.

        // The Insert method, inherited from Collection, takes an 
        // index and an OrderItem.
        Console.WriteLine("\nInsert(2, new OrderItem(...))");
        weekly.Insert(2, new 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}", 

        Console.WriteLine("\ncoweekly[2] = new OrderItem(...)");
        coweekly[2] = new 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.


        // Increase the quantity for a line item.
        Console.WriteLine("\ncoweekly(1) = New OrderItem(...)");
        coweekly[1] = new OrderItem(coweekly[1].PartNumber, 
            coweekly[1].Description, coweekly[1].Quantity + 1000, 



    private static void Display(SimpleOrder order)
        foreach( OrderItem item in order )

    private static void ChangedHandler(object source, 
        SimpleOrderChangedEventArgs e)

        OrderItem item = e.ChangedItem;

        if (e.ChangeType==ChangeType.Replaced)
            OrderItem replacement = e.ReplacedWith;

            Console.WriteLine("{0} (quantity {1}) was replaced " +
                "by {2}, (quantity {3}).", item.Description, 
                item.Quantity, replacement.Description, 
        else if(e.ChangeType == ChangeType.Cleared)
            Console.WriteLine("The order list was cleared.");
            Console.WriteLine("{0} (quantity {1}) was {2}.", 
                item.Description, item.Quantity, e.ChangeType);

// This class represents a simple line item in an order. All the 
// values are immutable except quantity.
public class OrderItem
    private int _partNumber;
    private string _description;
    private double _unitPrice;
    private int _quantity;

    public int PartNumber { get { return _partNumber; }}
    public string Description { get { return _description; }}
    public double UnitPrice { get { return _unitPrice; }}
    public int Quantity { get { return _quantity; }}

    public OrderItem(int partNumber, string description, int quantity, 
        double unitPrice)
        _partNumber = partNumber;
        _description = description;
        _quantity = quantity;
        _unitPrice = unitPrice;

    public override string ToString()
        return String.Format(
            "{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}", 
            PartNumber, _quantity, Description, UnitPrice, 
            UnitPrice * _quantity);

/* 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

Motor (quantity 10) was Removed.

Insert(2, new OrderItem(...))
Nut (quantity 10) was Added.

coweekly[2].Description: Nut

coweekly[2] = new OrderItem(...)
Nut (quantity 10) was replaced by Crank, (quantity 27).

IndexOf(temp): 2

Crank (quantity 27) was Removed.

Widget (quantity 400) was Removed.

coweekly(1) = New OrderItem(...)
Gear (quantity 175) was replaced by Gear, (quantity 1175).

110072675     27 Sprocket     at     5.30 =     143.10
110072684   1175 Gear         at     5.17 =   6,074.75

The order list was cleared.

.NET Framework

Supported in: 4, 3.5, 3.0, 2.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

Portable Class Library

Supported in: Portable Class Library

Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows XP SP2 x64 Edition, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.