C# 在C语言中处理可变集合键#

C# 在C语言中处理可变集合键#,c#,.net,collections,class-design,C#,.net,Collections,Class Design,假设我在C#中有一个简单的类Cat,其Name属性的类型为string。现在,我需要为我的猫创建一个集合类,所以我决定在自定义集合类中包装一个字典。基本上,此类保存一个私有字典变量,并根据需要添加或删除集合成员,以及按名称索引CAT: class Cats { private Dictionary<string, Cat> m_dic = new Dictionary<string,Cat>(); public void Add(Cat c) {

假设我在C#中有一个简单的类
Cat
,其
Name
属性的类型为
string
。现在,我需要为我的猫创建一个集合类,所以我决定在自定义集合类中包装一个
字典。基本上,此类保存一个私有字典变量,并根据需要添加或删除集合成员,以及按名称索引CAT:

class Cats
{
    private Dictionary<string, Cat> m_dic = new Dictionary<string,Cat>();

    public void Add(Cat c)
    {
        m_dic.Add(c.Name, c);
    }

    public void Remove(string name)
    {
        m_dic.Remove(name);
    }

    public Cat this[string name]
    {
        get
        {
            return m_dic[name];
        }
    }
}
我可以从它的名字中找到一只猫:

Cat c1 = cs["Sophie"];
这一切都很好。问题是,当我改变猫的名字时,就像这样:

Cats cs = new Cats();
cs.Add(new Cat("Valentina"));
cs.Add(new Cat("Sophie"));
cs.Add(new Cat("Tomboy"));
c1.Name = "Sofia";
class Cats
{
    private Dictionary<string, INotifyPropertyChanged> m_dic =
        new Dictionary<string, INotifyPropertyChanged>();
    public void Add(INotifyPropertyChanged obj)
    {
        m_dic.Add(obj.Name, obj);
    }
    public void Remove(string name)
    {
        m_dic.Remove(name);
    }
    public INotifyPropertyChanged this[string name]
    {
        get { return m_dic[name]; }
    }
}
class NotificationDictionary
{
    private Dictionary<string, INotificationKey> m_dic =
        new Dictionary<string, INotificationKey>();
    public void Add(INotificationKey obj)
    {
        m_dic.Add(obj.Key, obj);
    }
    public void Remove(string key)
    {
        m_dic.Remove(key);
    }
    public INotificationKey this[string key]
    {
        get { return m_dic[key]; }
    }
}
…显然,
c1
引用的对象的集合键没有更新。因此,如果我尝试使用同一项的新名称检索该项,则会出现异常:

Cat c2 = cs["Sofia"]; //KeyNotFoundException is thrown here.
这是运行时正确而明显的行为。我的问题是:您能否建议一种优雅可靠的方法,在元素的name属性发生更改时更改集合键

我的目标是能够在任何时候从名称中检索项目,正如您所想象的那样。我通过让
Name
属性的setter引发一个事件来解决这个问题,这样持有该对象的任何集合都可以更新相应的键。不过,这种方法相当麻烦,效率也不高


你能想出更好的吗?谢谢。

您的收藏将有多大,以及能够通过索引检索项目有多重要

如果它相对较小(几百个,而不是几千个),您最好使用
列表
,并使用新的LINQ扩展方法访问它们,如:

public Cat this[string name]{
    get{
        //Will return the first Cat in the list (or null if none is found)
        return m_List.Where(c => c.Name == name).FirstOrDefault();
    }
}
添加(和删除)也很简单:

public void Add(Cat c){
    m_List.Add(c);
}

如果这对你不起作用,请告诉我。希望这有帮助

您还可以在Cat上放置回调,以便在其Name属性更改时通知您的集合

…通过设置
名称
属性引发事件

你是说像这样的事吗

c1.Name = "Sofia";
NameChangedEventHandler handler = NameChangedEvent;
if (handler != null)
{
    handler(c1, new NameChangedEventArgs("Sophie", "Sophia"));
}
这就是你所说的让二传手发起一场比赛的意思吗?如果是这样,那么我建议将其移动到
Cat
类的
Name
属性设置器。没有理由要求二传手像这样发起比赛。当
Cat
的名称通过公共属性更改时,应隐式执行此操作

对我来说,这是一个优雅的解决方案;它只是不符合
字典
集合的工作方式。我不知道这本身就是个问题,但它确实将
Cats
集合与
Cat
类紧密耦合

请记住,您可能需要实现许多与generic
Dictionary
类相同的接口。否则,
Cats
集合将在某些方面类似于
字典
,但并不完全相同

编辑:这是对您的评论的回应。我希望我能更清楚地表达我的想法。我的目的是改进你的设计

我同意,一般来说,事件确实提供了更松散的耦合。但是,在这种情况下,
Cats
集合仍然与
Cat
类紧密耦合,因为集合正在注册由特定类型的类公开的特定类型的事件

那么,如何改进这一点呢

改进这一点的简单方法是让
Cat
类实现在接口中定义的事件。NET为这个明确的目的提供了这样一个接口,
System.ComponentModel
命名空间中的接口。通过在
Cat
类中实现此接口,这将允许
Cat
集合包装器定义如下:

Cats cs = new Cats();
cs.Add(new Cat("Valentina"));
cs.Add(new Cat("Sophie"));
cs.Add(new Cat("Tomboy"));
c1.Name = "Sofia";
class Cats
{
    private Dictionary<string, INotifyPropertyChanged> m_dic =
        new Dictionary<string, INotifyPropertyChanged>();
    public void Add(INotifyPropertyChanged obj)
    {
        m_dic.Add(obj.Name, obj);
    }
    public void Remove(string name)
    {
        m_dic.Remove(name);
    }
    public INotifyPropertyChanged this[string name]
    {
        get { return m_dic[name]; }
    }
}
class NotificationDictionary
{
    private Dictionary<string, INotificationKey> m_dic =
        new Dictionary<string, INotificationKey>();
    public void Add(INotificationKey obj)
    {
        m_dic.Add(obj.Key, obj);
    }
    public void Remove(string key)
    {
        m_dic.Remove(key);
    }
    public INotificationKey this[string key]
    {
        get { return m_dic[key]; }
    }
}
请注意,
INotificationKey
接口继承自
INotifyPropertyChanged
接口,该接口允许按如下方式定义集合包装:

Cats cs = new Cats();
cs.Add(new Cat("Valentina"));
cs.Add(new Cat("Sophie"));
cs.Add(new Cat("Tomboy"));
c1.Name = "Sofia";
class Cats
{
    private Dictionary<string, INotifyPropertyChanged> m_dic =
        new Dictionary<string, INotifyPropertyChanged>();
    public void Add(INotifyPropertyChanged obj)
    {
        m_dic.Add(obj.Name, obj);
    }
    public void Remove(string name)
    {
        m_dic.Remove(name);
    }
    public INotifyPropertyChanged this[string name]
    {
        get { return m_dic[name]; }
    }
}
class NotificationDictionary
{
    private Dictionary<string, INotificationKey> m_dic =
        new Dictionary<string, INotificationKey>();
    public void Add(INotificationKey obj)
    {
        m_dic.Add(obj.Key, obj);
    }
    public void Remove(string key)
    {
        m_dic.Remove(key);
    }
    public INotificationKey this[string key]
    {
        get { return m_dic[key]; }
    }
}
类通知字典
{
私人词典=
新字典();
公共作废添加(INotificationKey obj)
{
m_dic.Add(obj.Key,obj);
}
公共无效删除(字符串键)
{
拆卸(钥匙);
}
public INotificationKey此[字符串键]
{
获取{return m_dic[key];}
}
}
这是一个更加灵活的解决方案。但它仍有不足之处,因为它不像
字典那样完全发挥作用。例如,根据定义,
NotificationDictionary
类不能用于
foreach
迭代,因为它没有实现
IEnumerable
接口


要成为真正优雅的解决方案,集合的行为应该像一个
字典
。这需要在前端多做一点努力,但在后端,您会有一个足够灵活的解决方案,可以适应各种情况。

嘿,谢谢。我知道列表只通过int索引,所以使用Where这样的方法应该效率很低,对吗?我不希望有非常大的集合,所以这可能会起作用。“按int索引”应该是“按位置索引”;在我看来,pIt不是一个可怕的解决方案。正如我在回答中提到的,集合的大小将影响对象访问的速度。此外,如果列表非常大,并且您连续多次搜索某个元素,那么访问频率可能会对性能产生负面影响。我将尝试想出另一个与您的原始示例类似的解决方案!我在问题中解释说,我尝试让Cat在名称更改时引发一个事件,集合通过更新密钥来响应该事件。这就是你的意思吗?:-)我所做的(以及我在问题中试图解释的)正是您所建议的:从Cat的public Name属性中的set块引发事件。:-)耦合是可以的,因为毕竟事件并没有那么多地耦合类。收藏是免费的