KeyedCollection<TKey,TItem>.SetItem(Int32, TItem) 方法

定义

使用指定项替换指定索引处的项。

protected override void SetItem (int index, TItem item);

参数

index
Int32

要替换的项的从零开始的索引。

item
TItem

新项目。

示例

本部分包含两个代码示例,演示如何重写 SetItem 方法以提供设置 属性的 Collection<T>.Item[] 自定义行为。 第一个示例添加自定义通知事件,第二个示例提供对具有可变键的对象集合的支持。

示例 1

下面的代码示例演示如何重写受保护的 、、 和 方法,以便为 AddRemoveClear 方法提供自定义行为,以及如何在 C#) 中 (索引器设置默认Item[]属性。SetItemClearItemsRemoveItemInsertItem 此示例中提供的自定义行为是一个名为 Changed的通知事件,该事件在每个重写的方法的末尾引发。

代码示例创建 SimpleOrder 类,该类派生自 KeyedCollection<TKey,TItem> 并表示简单订单形式。 订单窗体包含 OrderItem 表示已排序项的对象。 该代码示例还创建一个 SimpleOrderChangedEventArgs 类来包含事件信息,并创建一个枚举来标识更改的类型。

该代码示例通过在 类的 方法中 Main 调用派生类的属性和方法 Demo 来演示自定义行为。

此代码示例使用具有不可变键的对象。 有关使用可变键的代码示例,请参阅 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];
        base.RemoveItem(index);

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

    protected override void ClearItems()
    {
        base.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
{
    Added,
    Removed,
    Replaced,
    Cleared
};

public class Demo
{
    public static void Main()
    {
        SimpleOrder weekly = new SimpleOrder();
        weekly.Changed += new
            EventHandler<SimpleOrderChangedEventArgs>(ChangedHandler);

        // 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));

        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, 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}",
            coweekly[2].Description);

        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.
        //
        Console.WriteLine("\nRemove(temp)");
        weekly.Remove(temp);

        Console.WriteLine("\nRemoveAt(0)");
        weekly.RemoveAt(0);

        // 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,
            coweekly[1].UnitPrice);

        Display(weekly);

        Console.WriteLine();
        weekly.Clear();
    }

    private static void Display(SimpleOrder order)
    {
        Console.WriteLine();
        foreach( OrderItem item in order )
        {
            Console.WriteLine(item);
        }
    }

    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,
                replacement.Quantity);
        }
        else if(e.ChangeType == ChangeType.Cleared)
        {
            Console.WriteLine("The order list was cleared.");
        }
        else
        {
            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

Remove(101030411)
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

Remove(temp)
Crank (quantity 27) was Removed.

RemoveAt(0)
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.
 */

示例 2

下面的代码示例演示如何重写受保护的 ChangeItemKey 方法以支持可变键,以及如何重写受保护的 InsertItemRemoveItemClearItemsSetItem 方法来维护密钥和集合的完整性。

该代码示例创建 MutableKeys 派生自 KeyedCollection<TKey,TItem>的 集合和 MutableKey 类。 类 MutableKey 具有可 Key 设置的属性。 将新键分配给 属性时,属性资源库调用 internal 集合的 Visual Basic) 方法中的 (FriendChangeKey ,以测试新键是否会与现有键冲突。 如果是这样,则会引发异常,并且不会更改属性值。

为了保持对象与集合之间的连接,并防止对象插入到两个MutableKey集合中,MutableKey类在 Visual Basic) Friend 字段中具有 (internalCollectionMutableKeys 此字段由受保护方法维护,这些方法提供用于在集合中添加和删除项的自定义行为,例如 InsertItem 方法。 字段在将项添加到集合时设置,并在删除该项时清除。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

// This class demonstrates one way to use the ChangeItemKey
// method to store objects with keys that can be changed. The
// ChangeItemKey method is used to keep the internal lookup
// Dictionary in sync with the keys of the stored objects.
//
// MutableKeys stores MutableKey objects, which have an Integer
// Key property that can be set. Therefore, MutableKeys inherits
// KeyedCollection(Of Integer, MutableKey).
//
public class MutableKeys : KeyedCollection<int, MutableKey>
{
    // This parameterless constructor delegates to the base class
    // constructor that specifies a dictionary threshold. A
    // threshold of 0 means the internal Dictionary is created
    // the first time an object is added.
    //
    public MutableKeys() : base(null, 0) {}

    protected override int GetKeyForItem(MutableKey item)
    {
        // The key is MutableKey.Key.
        return item.Key;
    }

    protected override void InsertItem(int index, MutableKey newItem)
    {
        if (newItem.Collection != null)
            throw new ArgumentException("The item already belongs to a collection.");

        base.InsertItem(index, newItem);
        newItem.Collection = this;
    }

    protected override void SetItem(int index, MutableKey newItem)
    {
        MutableKey replaced = Items[index];

        if (newItem.Collection != null)
            throw new ArgumentException("The item already belongs to a collection.");

        base.SetItem(index, newItem);
        newItem.Collection = this;
        replaced.Collection = null;
    }

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

        base.RemoveItem(index);
        removedItem.Collection = null;
    }

    protected override void ClearItems()
    {
        foreach( MutableKey mk in Items )
        {
            mk.Collection = null;
        }

        base.ClearItems();
    }

    internal void ChangeKey(MutableKey item, int newKey)
    {
        base.ChangeItemKey(item, newKey);
    }

    public void Dump()
    {
        Console.WriteLine("\nDUMP:");
        if (Dictionary == null)
        {
            Console.WriteLine("    The dictionary has not been created.");
        }
        else
        {
            Console.WriteLine("    Dictionary entries");
            Console.WriteLine("    ------------------");

            foreach( KeyValuePair<int, MutableKey> kvp in Dictionary )
            {
                Console.WriteLine("    {0} : {1}", kvp.Key, kvp.Value);
            }
        }

        Console.WriteLine("\n    List of items");
        Console.WriteLine("    -------------");

        foreach( MutableKey mk in Items )
        {
            Console.WriteLine("    {0}", mk);
        }
    }
}

public class Demo
{
    public static void Main()
    {
        MutableKeys mkeys = new MutableKeys();

        // The Add method is inherited from Collection.
        //
        mkeys.Add(new MutableKey(110072674, "Widget"));
        mkeys.Add(new MutableKey(110072675, "Sprocket"));

        mkeys.Dump();

        Console.WriteLine("\nCreate and insert a new item:");
        MutableKey test = new MutableKey(110072684, "Gear");
        mkeys.Insert(1, test);

        mkeys.Dump();

        try
        {
            Console.WriteLine("\nTry to insert the item again:");
            mkeys.Insert(1, test);
        }
        catch(ArgumentException ex)
        {
            Console.WriteLine("Error: {0}", ex.Message);
        }

        Console.WriteLine("\nChange the Key property of the item:");
        test.Key = 100000072;

        mkeys.Dump();

        try
        {
            Console.WriteLine("\nTry to set the Key property to an existing key:");
            test.Key = 110072674;
        }
        catch(ArgumentException ex)
        {
            Console.WriteLine("Error: {0}", ex.Message);
        }

        mkeys.Dump();
    }

    private static void Display(MutableKeys order)
    {
        Console.WriteLine();
        foreach( MutableKey item in order )
        {
            Console.WriteLine(item);
        }
    }
}

// This class has a key that can be changed.
//
public class MutableKey
{

    public MutableKey(int newKey, string newValue)
    {
        _key = newKey;
        Value = newValue;
    } //New

    public string Value;
    internal MutableKeys Collection;

    private int _key;
    public int Key
    {
        get
        {
            return _key;
        }
        set
        {
            if (Collection != null)
            {
                Collection.ChangeKey(this, value);
            }

            _key = value;
        }
    }

    public override string ToString()
    {
        return String.Format("{0,9} {1}", _key, Value);
    }
}

/* This code example produces the following output:

DUMP:
    Dictionary entries
    ------------------
    110072674 : 110072674 Widget
    110072675 : 110072675 Sprocket

    List of items
    -------------
    110072674 Widget
    110072675 Sprocket

Create and insert a new item:

DUMP:
    Dictionary entries
    ------------------
    110072674 : 110072674 Widget
    110072675 : 110072675 Sprocket
    110072684 : 110072684 Gear

    List of items
    -------------
    110072674 Widget
    110072684 Gear
    110072675 Sprocket

Try to insert the item again:
Error: The item already belongs to a collection.

Change the Key property of the item:

DUMP:
    Dictionary entries
    ------------------
    110072674 : 110072674 Widget
    110072675 : 110072675 Sprocket
    100000072 : 100000072 Gear

    List of items
    -------------
    110072674 Widget
    100000072 Gear
    110072675 Sprocket

Try to set the Key property to an existing key:
Error: An item with the same key has already been added.

DUMP:
    Dictionary entries
    ------------------
    110072674 : 110072674 Widget
    110072675 : 110072675 Sprocket
    100000072 : 100000072 Gear

    List of items
    -------------
    110072674 Widget
    100000072 Gear
    110072675 Sprocket
 */

注解

查找字典会相应地更新。 也就是说,从查找字典中删除要替换的项的键,并添加新项的键。

此方法是 O (1) 操作。

实现者须知

重写此方法以提供用于设置 Item[] 从泛型类继承的属性的 Collection<T> 自定义行为。

备注

此方法不会影响 属性的行为 KeyedCollection<TKey,TItem>.Item[] ,属性是只读的。

调用此方法的基类实现以设置基础集合中的项并更新查找字典。

适用于

产品 版本
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7, 8, 9
.NET Framework 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0

另请参阅