Я хочу создать хранилище данных, чтобы позволить мне хранить некоторые данные.
Первая идея состояла в том, чтобы создать словарь, где у Вас есть 1 ключ со многими значениями, так немного как связь "один ко многим".
Я думаю, что словарь только имеет 1 значение ключа.
Как еще я мог хранить эту информацию?
С .net3.5 + вместо того, чтобы использовать Dictionary<IKey, List<IValue>>
можно использовать Lookup
от пространства имен Linq:
// lookup Order by payment status (1:m)
// would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed
ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed);
IEnumerable<Order> payedOrders = byPayment[false];
От msdn:
Поиск А напоминает Словарь. Различие - то, что Словарь отображает ключи к единственным значениям, тогда как Поиск отображает ключи к наборам значений.
можно создать экземпляр Поиска путем вызова ToLookup на объекте, который реализует IEnumerable.
можно также хотеть читать этот ответ к связанный вопрос . Для большего количества информации консультируйтесь msdn.
Полный пример:
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqLookupSpike
{
class Program
{
static void Main(String[] args)
{
// init
var orderList = new List<Order>();
orderList.Add(new Order(1, 1, 2010, true));//(orderId, customerId, year, isPayed)
orderList.Add(new Order(2, 2, 2010, true));
orderList.Add(new Order(3, 1, 2010, true));
orderList.Add(new Order(4, 2, 2011, true));
orderList.Add(new Order(5, 2, 2011, false));
orderList.Add(new Order(6, 1, 2011, true));
orderList.Add(new Order(7, 3, 2012, false));
// lookup Order by its id (1:1, so usual dictionary is ok)
Dictionary<Int32, Order> orders = orderList.ToDictionary(o => o.OrderId, o => o);
// lookup Order by customer (1:n)
// would need something like Dictionary<Int32, IEnumerable<Order>> orderIdByCustomer
ILookup<Int32, Order> byCustomerId = orderList.ToLookup(o => o.CustomerId);
foreach (var customerOrders in byCustomerId)
{
Console.WriteLine("Customer {0} ordered:", customerOrders.Key);
foreach (var order in customerOrders)
{
Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed);
}
}
// the same using old fashioned Dictionary
Dictionary<Int32, List<Order>> orderIdByCustomer;
orderIdByCustomer = byCustomerId.ToDictionary(g => g.Key, g => g.ToList());
foreach (var customerOrders in orderIdByCustomer)
{
Console.WriteLine("Customer {0} ordered:", customerOrders.Key);
foreach (var order in customerOrders.Value)
{
Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed);
}
}
// lookup Order by payment status (1:m)
// would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed
ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed);
IEnumerable<Order> payedOrders = byPayment[false];
foreach (var payedOrder in payedOrders)
{
Console.WriteLine("Order {0} from Customer {1} is not payed.", payedOrder.OrderId, payedOrder.CustomerId);
}
}
class Order
{
// key properties
public Int32 OrderId { get; private set; }
public Int32 CustomerId { get; private set; }
public Int32 Year { get; private set; }
public Boolean IsPayed { get; private set; }
// additional properties
// private List<OrderItem> _items;
public Order(Int32 orderId, Int32 customerId, Int32 year, Boolean isPayed)
{
OrderId = orderId;
CustomerId = customerId;
Year = year;
IsPayed = isPayed;
}
}
}
}
Комментарий по Неизменности
По умолчанию, Поиски довольно неизменны и получающие доступ internal
, с включила бы отражение. Если Вы нуждаетесь в переменчивости и не хотите писать свою собственную обертку, Вы могли бы использовать MultiValueDictionary
(раньше известный как [1 113] MultiDictionary
) от [1 114] corefxlab (раньше часть [1 115] Microsoft.Experimental.Collections
, который больше не обновляется).
Можно создать очень упрощенный мультисловарь, который автоматизирует к процессу вставки значений как это:
public class MultiDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>>
{
public void Add(TKey key, TValue value)
{
if (TryGetValue(key, out List<TValue> valueList)) {
valueList.Add(value);
} else {
Add(key, new List<TValue> { value });
}
}
}
Это создает перегруженную версию Add
метод. Исходный позволяет Вам вставлять список объектов для ключа, если никакая запись для этой записи еще не существует. Эта версия позволяет Вам вставлять единственный объект в любом случае.
Можно также использовать;
List<KeyValuePair<string, string>> Mappings;
Вот мой подход для достижения этого поведения.
Для более комплексного решения, включающего ILookup<TKey, TElement>
, проверьте мой другой ответ .
public abstract class Lookup<TKey, TElement> : KeyedCollection<TKey, ICollection<TElement>>
{
protected override TKey GetKeyForItem(ICollection<TElement> item) =>
item
.Select(b => GetKeyForItem(b))
.Distinct()
.SingleOrDefault();
protected abstract TKey GetKeyForItem(TElement item);
public void Add(TElement item)
{
var key = GetKeyForItem(item);
if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
collection.Add(item);
else
Add(new List<TElement> { item });
}
public void Remove(TElement item)
{
var key = GetKeyForItem(item);
if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
{
collection.Remove(item);
if (collection.Count == 0)
Remove(key);
}
}
}
Использование:
public class Item
{
public string Key { get; }
public string Value { get; set; }
public Item(string key, string value = null) { Key = key; Value = value; }
}
public class Lookup : Lookup<string, Item>
{
protected override string GetKeyForItem(Item item) => item.Key;
}
static void Main(string[] args)
{
var toRem = new Item("1", "different");
var single = new Item("2", "single");
var lookup = new Lookup()
{
new Item("1", "hello"),
new Item("1", "hello2"),
new Item(""),
new Item("", "helloo"),
toRem,
single
};
lookup.Remove(toRem);
lookup.Remove(single);
}
Примечание: ключ должен быть неизменным (или удалить и повторно добавить на ключевое изменение).
Смотрите на MultiValueDictionary от Microsoft.
Пример кода:
MultiValueDictionary<string, string> Parameters = new MultiValueDictionary<string, string>();
Parameters.Add("Malik", "Ali");
Parameters.Add("Malik", "Hamza");
Parameters.Add("Malik", "Danish");
//Parameters["Malik"] now contains the values Ali, Hamza, and Danish
Используйте это:
Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
Microsoft просто добавила официальную версию перед арендным договором точно, что Вы ищете (названный MultiDictionary) доступный через NuGet здесь: https://www.nuget.org/packages/Microsoft. Экспериментальный. Наборы /
Информация об использовании и большем количестве деталей могут быть найдены через официальное сообщение в блоге MSDN здесь: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx
я - разработчик для этого пакета, таким образом сообщите мне или здесь или на MSDN, если у Вас есть какие-либо вопросы о производительности или чем-нибудь.
Hope, которая помогает.
Обновление
Эти MultiValueDictionary
находится теперь на corefxlab repo, и можно получить пакет NuGet от этот канал MyGet.