C#で定数辞書を作成する


128

sからs への定数(実行時に変更されない)マッピングを作成する最も効率的な方法は何ですか?stringint

私はconst辞書を使ってみましたが、うまくいきませんでした。

適切なセマンティクスで不変のラッパーを実装することもできますが、それでも完全に正しいとは言えません。


質問した人のために、私は生成されたクラスにIDataErrorInfoを実装していて、記述子の配列にcolumnNameルックアップを行う方法を探しています。

スイッチで文字列を受け入れることを(テストではタイプミス!)気づかなかったので、これを使用します。ありがとう!


1
解決策はここにあります: stackoverflow.com/questions/10066708/...

回答:


180

C#で本当にコンパイル時に生成される定数ディクショナリを作成することは、実際には簡単な作業ではありません。実際、ここでの答えはどれも実際にはそれを実現しません。

ただし、要件を満たすソリューションは1つありますが、必ずしも適切なソリューションとは限りません。C#仕様に従って、switch-caseテーブルは定数ハッシュジャンプテーブルにコンパイルされることに注意してください。つまり、それらは定数辞書であり、一連のif-elseステートメントではありません。したがって、次のようなswitch-caseステートメントを検討してください。

switch (myString)
{
   case "cat": return 0;
   case "dog": return 1;
   case "elephant": return 3;
}

これはまさにあなたが望むものです。そして、はい、それは醜いです。


9
それはとにかく生成されたコードであり、優れたパフォーマンス特性を持ち、コンパイル時に計算でき、他にも私のユースケースに適したプロパティがあります。こうやってやる!
デビッドシュミット

4
このように値以外の型を返すと、クラスの不変性が壊れるので注意してください。
トムアンダーソン

35
switch-caseは、値を "foreach"する必要があるまで素晴らしいです。
アレックス

6
それは辞書が持っている多くの素晴らしい機能を欠いています。
Zinan Xing

1
@TomAnderson:返されたアイテムが値型(可変または可変)である場合、またはそれらが不変のクラスオブジェクトである場合、構成は不変として動作します。
スーパーキャット2015年

37

現在のフレームワークには、貴重な少数の不変コレクションがあります。.NET 3.5の比較的痛みのないオプションを1つ考えることができます。

使用Enumerable.ToLookup()- Lookup<,>クラスは不変です(ただしrhsでは複数値)。あなたはこれをDictionary<,>非常に簡単に行うことができます:

    Dictionary<string, int> ids = new Dictionary<string, int> {
      {"abc",1}, {"def",2}, {"ghi",3}
    };
    ILookup<string, int> lookup = ids.ToLookup(x => x.Key, x => x.Value);
    int i = lookup["def"].Single();

15
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");

2
面白いアイデア!Parse()呼び出しのパフォーマンスはどれくらいかと思います。プロファイラーしか答えられないのではないかと心配です。
デビッドシュミット

10

これは、「CONST辞書」に到達できる最も近いものです。

public static int GetValueByName(string name)
{
    switch (name)
    {
        case "bob": return 1;
        case "billy": return 2;
        default: return -1;
    }
}

コンパイラーは、コードを可能な限りクリーンに構築するのに十分なほどスマートになります。


8

4.5+フレームワークを使用している場合、ReadOnlyDictionary(リストの場合もReadOnlyコレクション)を使用して、読み取り専用のマッピング/定数を作成します。以下のように実装されています。

static class SomeClass
{
    static readonly ReadOnlyDictionary<string,int> SOME_MAPPING 
        = new ReadOnlyDictionary<string,int>(
            new Dictionary<string,int>()
            {
                { "One", 1 },
                { "Two", 2 }
            }
        )
}        

private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, };これは私がやった方法です。
Sanket Sonavane

@SanketSonavane aはとreadonly Dictionary<TKey, TValue>同等ではありませんReadOnlyDictionary<TKey, TValue>。実際、それらが「読み取り専用」である方法は、お互いにかなり反対です。参照:dotnetfiddle.net/v0l4aA
Broots Waymb

2

名前空間またはクラスを使用して値をネストしないのはなぜですか?不完全かもしれませんが、とても綺麗です。

public static class ParentClass
{
    // here is the "dictionary" class
    public static class FooDictionary
    {
        public const string Key1 = "somevalue";
        public const string Foobar = "fubar";
    }
}

これで、.ParentClass.FooDictionary.Key1などにアクセスできます。


これはIDataErrorInfoインターフェイスを実装していないためです。
デビッドシュミット

1

辞書には標準の不変のインターフェースがないように思われるので、残念ながらラッパーを作成することは唯一の合理的なオプションのように思えます。

編集:Marc Gravellが私が見逃したILookupを見つけました-.ToLookup()で辞書を変換する必要はありますが、少なくとも新しいラッパーの作成を回避できます。

これが特定のシナリオに制限されたニーズである場合、よりビジネスロジック指向のインターフェイスを使用する方がよいでしょう。

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}

1

なぜ誰もこれについて言及しなかったのかわかりませんが、C#ではconstを割り当てることができないため、静的な読み取り専用プロパティを使用しています。

例:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };

辞書の内容はまだ変更可能であり、実行時に割り当てられるためです。
デビッドシュミット

0

私がwinformsコンボボックスにバインドしているので、ちょうど別のアイデア:

public enum DateRange {
    [Display(Name = "None")]
    None = 0,
    [Display(Name = "Today")]
    Today = 1,
    [Display(Name = "Tomorrow")]
    Tomorrow = 2,
    [Display(Name = "Yesterday")]
    Yesterday = 3,
    [Display(Name = "Last 7 Days")]
    LastSeven = 4,
    [Display(Name = "Custom")]
    Custom = 99
    };

int something = (int)DateRange.None;

表示名から int値を取得するに

public static class EnumHelper<T>
{
    public static T GetValueFromName(string name)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();

        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DisplayAttribute)) as DisplayAttribute;
            if (attribute != null)
            {
                if (attribute.Name == name)
                {
                    return (T)field.GetValue(null);
                }
            }
            else
            {
                if (field.Name == name)
                    return (T)field.GetValue(null);
            }
        }

        throw new ArgumentOutOfRangeException("name");
    }
}

使用法:

var z = (int)EnumHelper<DateRange>.GetValueFromName("Last 7 Days");

-2

何故なの:

public class MyClass
{
    private Dictionary<string, int> _myCollection = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };

    public IEnumerable<KeyValuePair<string,int>> MyCollection
    {
        get { return _myCollection.AsEnumerable<KeyValuePair<string, int>>(); }
    }
}

3
これは、指定された条件下で利用可能な代替手段と比較して非常に高価であるためです。
David Schmitt、2007

また、これでもコンパイルされないため
AustinWBryan

@AustinWBryanはい、コンパイルされます
derHugo '17
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.