C#のオブジェクト初期化ルーチンでプロパティの新しいルックアップクラスを宣言するにはどうすればよいですか?
例えば。
new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> }
カテゴリビットは常にコンパイルエラーを受け取ります。
Lookup<int, string>
タイプのCategory
というプロパティがあり、このプロパティを次の方法でインスタンス化したいと思います。
new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> };
しかし、コンパイルエラーを乗り越えることはできません。
MSDNのドキュメントによると、Lookup
クラスのパブリックコンストラクターはありません: http://msdn.Microsoft.com/en-us/library/bb460184.aspx
Lookup<TKey, TElement>
を実装するオブジェクトでToLookup
を呼び出すことにより、IEnumerable<T>
のインスタンスを作成できます。
あなたは次のようなことをしたいと思うでしょう:
new Component { ID = 1, Name = "MOBO", Category = new[] { … }.ToLookup(…) }
コメントに対処するための更新:
カテゴリ情報をどこから取得しているのかわからないので、何か作ります…
new Component {
ID = 1,
Name = "MOBO",
Category = new Dictionary<int, string> {
{ 3, "Beverages" }
{ 5, "Produce" }
}.ToLookup(o => o.Key, o => o.Value)
}
私の推測では、あなたのカテゴリは、私がここで行ったように辞書をインスタンス化するのではなく、他のソースから取得されると思います。
から [〜#〜] msdn [〜#〜] :
Lookup<TKey, TElement>
の新しいインスタンスを作成するためのパブリックコンストラクターはありません。
さらに、Lookup<TKey, TElement>
オブジェクトは不変です。つまり、作成後にLookup<TKey, TElement>
オブジェクトに要素やキーを追加または削除することはできません。
これが私の試みです。キーが不変であることを確認してください( 要点 )。
public class MultiValueDictionary<TKey, TElement>
: Collection<TElement>, ILookup<TKey, TElement>
{
public MultiValueDictionary(Func<TElement, TKey> keyForItem)
: base(new Collection(keyForItem))
{
}
new Collection Items => (Collection)base.Items;
public IEnumerable<TElement> this[TKey key] => Items[key];
public bool Contains(TKey key) => Items.Contains(key);
IEnumerator<IGrouping<TKey, TElement>>
IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator() => Items.GetEnumerator();
class Collection
: KeyedCollection<TKey, Grouping>, IEnumerable<TElement>, IList<TElement>
{
Func<TElement, TKey> KeyForItem { get; }
public Collection(Func<TElement, TKey> keyForItem) => KeyForItem = keyForItem;
protected override TKey GetKeyForItem(Grouping item) => item.Key;
public void Add(TElement item)
{
var key = KeyForItem(item);
if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
collection.Add(item);
else
Add(new Grouping(key) { item });
}
public bool Remove(TElement item)
{
var key = KeyForItem(item);
if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)
&& collection.Remove(item))
{
if (collection.Count == 0)
Remove(key);
return true;
}
return false;
}
IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
{
foreach (var group in base.Items)
foreach (var item in group)
yield return item;
}
const string IndexError = "Indexing not supported.";
public int IndexOf(TElement item) => throw new NotSupportedException(IndexError);
public void Insert(int index, TElement item) => Add(item);
public bool Contains(TElement item) => Items.Contains(item);
public void CopyTo(TElement[] array, int arrayIndex) =>
throw new NotSupportedException(IndexError);
new IEnumerable<TElement> Items => this;
public bool IsReadOnly => false;
TElement IList<TElement>.this[int index]
{
get => throw new NotSupportedException(IndexError);
set => throw new NotSupportedException(IndexError);
}
}
class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
{
public Grouping(TKey key) => Key = key;
public TKey Key { get; }
}
}
何らかの理由で空のILookup
を返す必要がある場合は、空の辞書から返すことができます。たとえば、ILookup<string, int>
を作成するには、次を使用できます。
return new Dictionary<string, int>().ToLookup(kvp => kvp.Key, kvp => kvp.Value);
残念ながら、これはILookup
を自分で実装するクラスを作成せずにそれを行うために私が見ることができる最も簡潔な方法です。
ToLookupだけを使用することはできません。キーと値を見つける方法を説明する必要があります。
// from ChaosPandion's code
using System.Linq; // make sure you have the using statement
var component = new Component()
{
ID = 1,
Name = "MOBO",
Category = (Lookup<int, string>)
(new Dictionary<int, string>() { {1, "one"} })
.ToLookup(p=>p.Key, p=>p.Value)
}
ただし、辞書の代わりにここでルックアップを使用する理由がわかりません。
ルックアップは辞書と同じ概念で機能しますが、違いは、辞書が単一の値へのキーをマップするのに対し、ルックアップは多くの値へのキーをマップすることです。
これは、次のことも意味します。
ILookup<string, Category>
次のように見ることができます:
IDictionary<string, IEnumerable<Category>>
基本的に、多くのオブジェクト/値を同じキーにマップする場合は、ILookup
を使用する必要があります。オブジェクトの任意のリストからILookup
を作成できます。ここで、これらのオブジェクトをいくつかのプロパティでグループ化します。見る:
public class Product
{
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
var products = new List<Product>();
products.Add(new Product { Name = "TV", Price = 400, Category = "Electronics" });
products.Add(new Product { Name = "Computer", Price = 900, Category = "Electronics" });
products.Add(new Product { Name = "Keyboard", Price = 50, Category = "Electronics" });
products.Add(new Product { Name = "Orange", Price = 2, Category = "Fruits" });
products.Add(new Product { Name = "Grape", Price = 3, Category = "Fruits" });
// group by category
ILookup<string, Product> lookup = products.ToLookup(prod => prod.Category);
foreach (var item in lookup)
{
// this first loop would run two times
// because there are two categories: Electronics and Fruits
string category = item.Key;
decimal totalPriceForCategory = item.Sum(i => i.Price);
foreach (var product in item)
{
// for the electronics, this would loop three times
// for the fruits, this would loop two times
string name = product.Name;
decimal price = product.Price;
}
}
次のようなカテゴリのすべての製品を入手することもできます。
IEnumerable<Product> eletronics = lookup["Electronics"];
IEnumerable<Product> fruits = lookup["Fruits"];