简体   繁体   English

C# 集合按属性索引?

[英]C# collection indexed by property?

A problem I often run into is needing to store a collection of objects in such a way that I can retrieve them by a particular field/property that is a unique "index" for that object.我经常遇到的一个问题是需要以这样一种方式存储对象集合,以便我可以通过特定字段/属性检索它们,该字段/属性是该 object 的唯一“索引”。 For example, I have a Person object for which the name field is a unique identifier, and I want to be able to retrieve from some collection of Person objects the Person whose name="Sax Russell" .例如,我有一个Person object,它的name字段是唯一标识符,我希望能够从一些Person对象集合中检索name="Sax Russell"Person In Java I usually accomplish this by using a Map where I actually want a Set , and always using the "index" field of the object as its key in the map, ie peopleMap.add(myPerson.getName(), myPerson) . In Java I usually accomplish this by using a Map where I actually want a Set , and always using the "index" field of the object as its key in the map, ie peopleMap.add(myPerson.getName(), myPerson) . I was thinking of doing the same thing in C# with Dictionary s, like this:我正在考虑在 C# 中使用Dictionary做同样的事情,如下所示:

class Person {
    public string Name {get; set;}
    public int Age {get; set;}
    //...
}

Dictionary<string, Person> PersonProducerMethod() {
    Dictionary<string, Person> people = new Dictionary<string, Person>();
    //somehow produce Person instances...
    people.add(myPerson.Name, myPerson);
    //...
    return people;
}

void PersonConsumerMethod(Dictionary<string, Person> people, List<string> names) {
    foreach(var name in names) {
        person = people[name];
        //process person somehow...
    }
}

However, this seems clumsy, and introduces a rather loose coupling between the keys of the Dictionary and its values;然而,这看起来很笨拙,并且在Dictionary的键和它的值之间引入了相当松散的耦合; I implicitly depend on every producer of Person dictionaries using the Name property as the key under which to store each Person .我隐含地依赖于Person字典的每个生产者,使用Name属性作为存储每个Person的键。 I have no guarantee that the element at people["Sax Russell"] is actually a Person with Name="Sax Russell" unless I double-check every time I access the dictionary.我不能保证people["Sax Russell"]的元素实际上是Name="Sax Russell"Person ,除非我每次访问字典时都仔细检查。

Might there be some way to explicitly ensure that my collection of Person objects is indexed by name, using custom equality comparers and/or LINQ queries?是否有某种方法可以使用自定义相等比较器和/或 LINQ 查询来明确确保我的Person对象集合按名称索引? It's important that lookup stay constant-time, which is why I can't just use List.Find or Enumerable.Where .查找保持恒定时间很重要,这就是为什么我不能只使用List.FindEnumerable.Where I've tried using a HashSet and constructing it with an equality comparer that compares just the Name field of the objects it's given, but there doesn't seem to be any way to then retrieve the Person objects using just their name.我已经尝试使用HashSet并使用相等比较器构造它,该比较器仅比较它给出的对象的Name字段,但似乎没有任何方法可以仅使用它们的名称检索Person对象。

I'm not sure if there's anything built-in that does what you want, but there's nothing stopping you from wrapping a dictionary specifying the key yourself, and implementing IList<Person> . 我不确定是否有任何内置功能可以满足您的需求,但是没有什么可以阻止您自己包装指定密钥的字典,并实现IList<Person> The key here (no pun intended) is the consumer does not has access to the underlying dictionary so you can be assured the keys are accurate. 这里的关键(没有双关语)是消费者无法访问基础字典,因此您可以确保密钥是准确的。

Part of the implementation might look like the following, note the custom indexer as well: 部分实现可能如下所示,请注意自定义索引器:

public partial class PersonCollection : IList<Person>
{

    //the underlying dictionary
    private Dictionary<string, Person> _dictionary;

    public PersonCollection()
    {
        _dictionary = new Dictionary<string, Person>();
    }

    public void Add(Person p)
    {
        _dictionary.Add(p.Name, p);
    }

    public Person this[string name]
    {
        get
        {
            return _dictionary[name];
        }
    }

}

As a side bonus, you are also free to change the implementation later without having to change the consuming code. 作为附带奖励,您也可以在以后更改实施,而无需更改消耗代码。

You can build your own collection backed by a dictionary to accomplish this task. 您可以构建由字典支持的自己的集合来完成此任务。 The idea is to store a delegate that takes a Person and returns a string by reading the Name property. 我们的想法是存储一个带有Person的委托,并通过读取Name属性返回一个字符串。

Here is a skeletal solution of such a collection: 这是一个这样的集合的骨架解决方案:

public class PropertyMap<K,V> : ICollection<V> {
    private readonly IDictionary<K,V> dict = new Dictionary<K,V>();
    private readonly Func<V,K> key;
    public PropertyMap(Func<V,K> key) {
        this.key = key;
    }
    public void Add(V v) {
        dict.Add(key(v));
    }
    // Implement other methods of ICollection
    public this[K k] {
        get { return dict[k]; }
        set { dict[k] = value; }
    }
}

Here is how to use it: 以下是如何使用它:

PropertyMap<string,Person> mp = new PropertyMap<string,Person>(
    p => p.Name
);
mp.Add(p1);
mp.Add(p2);

If your property value is NOT UNIQUE and you still want to "index" a collection by that property (for example by a person's first name, so that you can quickly find everyone named "Bob" in your people collection) - then you can use the .NET built-in Lookup class as described in this question: Duplicate keys in .NET dictionaries?如果您的属性值不是唯一的,并且您仍想按该属性“索引”一个集合(例如,通过一个人的名字,以便您可以快速找到您的人员集合中名为“Bob”的每个人) - 那么您可以使用.NET 内置Lookup class 如以下问题所述: .NET 字典中的重复键?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM