C # collection indexed by property?

The problem that I often encounter is the need to store a collection of objects so that I can get them by a specific field / property, which is a unique "index" for this 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 on Person whose name="Sax Russell" . In Java, I usually do this with Map where I actually want Set , and always use the "index" field of the object as its key on the map, i.e. peopleMap.add(myPerson.getName(), myPerson) . I was thinking of doing the same in C # with Dictionary s, for example:

 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 awkward and represents a rather weak connection between the Dictionary keys and its values; I am implicitly dependent on each Person dictionary producer using the Name property as the key to store each Person . I have no guarantee that the element in people["Sax Russell"] is actually a Person with Name="Sax Russell" if I do not double-check every time I access the dictionary.

Could there be some way to explicitly ensure that my collection of Person objects is indexed by name using custom equality comparisons and / or LINQ queries? It is important that the search remains constant, so I cannot just use List.Find or Enumerable.Where . I tried using a HashSet and creating it using an equality comparator, which compares only the Name field of the objects it gave, but there seems to be no way to then retrieve Person objects using only their name.

+6
source share
2 answers

You can create your own dictionary-supported collection for this task. The idea is to save the delegate that accepts Person and return a string by reading the Name property.

Here is a skeletal solution to 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's how to use it:

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

I'm not sure if there is anything built-in that does what you want, but it doesn't bother you to wrap the dictionary defining the key and implement IList<Person> . The key here (no pun intended) is that the consumer does not have access to the basic dictionary, so you can be sure that the keys are accurate.

Part of the implementation might look like this: pay attention to the custom index:

 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 bonus, you can also change the implementation later without changing the consumption code.

+6
source

Source: https://habr.com/ru/post/948986/


All Articles