C # 'da sabit bir sözlük oluşturma


128

S ' nin s' ye sabit (çalışma zamanında asla değişmez) eşlemesi oluşturmanın en etkili yolu nedir ?stringint

Sabit Sözlük kullanmayı denedim ama işe yaramadı.

Uygun semantiğe sahip değişmez bir sarmalayıcı uygulayabilirim , ancak bu yine de tamamen doğru görünmüyor.


Bunu soranlar için , IDataErrorInfo'yu oluşturulmuş bir sınıfta uyguluyorum ve tanımlayıcı dizimde columnName araması yapmanın bir yolunu arıyorum.

Anahtarın dizeleri kabul ettiğinin farkında değildim (test ederken yazım hatası! D'oh!), Bu yüzden kullanacağım şey bu. Teşekkürler!


1
burada bir çözüm var: stackoverflow.com/questions/10066708/…

Yanıtlar:


181

C # ile gerçek bir derleme zamanı oluşturulmuş sabit sözlük oluşturmak gerçekten kolay bir iş değildir. Aslında, buradaki cevapların hiçbiri gerçekten bunu başaramıyor.

Güzel bir çözüm olmasa da gereksinimlerinizi karşılayan bir çözüm var; C # spesifikasyonuna göre, anahtar durum tablolarının sabit hızlı atlama tablolarına göre derlendiğini unutmayın. Yani, bunlar sabit sözlüklerdir, bir dizi if-else ifadesi değil. Öyleyse şuna benzer bir durum değiştirme ifadesini düşünün:

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

Bu tam olarak istediğin şey. Ve evet, biliyorum, bu çirkin.


9
Yine de kod üretildi, iyi performans özelliklerine sahip, derleme zamanı hesaplanabilir ve benim kullanım durumum için başka güzel özelliklere de sahip. Bunu böyle yapacağım!
David Schmitt

4
Değersiz türleri döndürmenin, bunun gibi bile sınıflarınızın değişmezliğini bozacağına dikkat edin.
Tom Anderson

35
Değerler arasında "her şeyi" tamamlamanız gerekene kadar anahtar durum harikadır.
Alex

6
Sözlüklerin sahip olduğu pek çok güzel işlevden yoksundur.
Zinan Xing

1
@TomAnderson: Yapı, döndürülen öğeler değer türleriyse (değişebilir veya değil) veya değişmez sınıf nesneleri ise, değişmez olarak davranacaktır.
supercat

37

Mevcut çerçevede değerli birkaç değişmez koleksiyon var. NET 3.5'te nispeten ağrısız bir seçenek düşünebilirim:

Kullanım Enumerable.ToLookup()- Lookup<,>sınıf değişmezdir (ancak rhs üzerinde çok değerlidir); bunu Dictionary<,>oldukça kolay bir şekilde yapabilirsiniz :

    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
İlginç fikir! Parse () çağrısının ne kadar iyi performans gösterdiğini merak ediyorum. Korkarım sadece bir profilci buna cevap verebilir.
David Schmitt

10

Bu, bir "CONST Sözlüğüne" ulaşabileceğiniz en yakın şeydir:

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

Derleyici, kodu olabildiğince temiz oluşturacak kadar akıllı olacaktır.


8

4.5+ Framework kullanıyorsanız, salt okunur eşlemeler / sabitler yapmak için ReadOnlyDictionary (ayrıca listeler için ReadOnly Collection) kullanırdım. Aşağıdaki şekilde uygulanır.

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 }, };Ben böyle yaptım.
Sanket Sonavane

@SanketSonavane a readonly Dictionary<TKey, TValue>, bir ReadOnlyDictionary<TKey, TValue>. Aslında, "salt okunur" olmaları birbirine oldukça zıttır. Bakınız: dotnetfiddle.net/v0l4aA .
Broots Waymb

2

Değerlerinizi iç içe yerleştirmek için neden ad alanları veya sınıflar kullanmıyorsunuz? Kusurlu olabilir ama çok temiz.

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

Artık .ParentClass.FooDictionary.Key1, vb. Erişebilirsiniz.


Çünkü bu, IDataErrorInfo arabirimini uygulamaz.
David Schmitt

1

Sözlükler için standart değişmez bir arayüz yok gibi görünüyor, bu nedenle bir sarmalayıcı oluşturmak maalesef tek makul seçenek gibi görünüyor.

Düzenle : Marc Gravell kaçırdığım ILookup'ı buldu - bu, en azından yeni bir sarmalayıcı oluşturmaktan kaçınmanıza izin verecek, ancak yine de Sözlüğü .ToLookup () ile dönüştürmeniz gerekiyor.

Bu, belirli bir senaryo ile sınırlı bir ihtiyaçsa, daha iş mantığı odaklı bir arayüzle daha iyi durumda olabilirsiniz:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}

1

Neden kimsenin bundan bahsetmediğinden emin değilim ama C # 'da const atayamadığım şeyler için statik salt okunur özellikler kullanıyorum.

Misal:

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

çünkü sözlüğün içeriği hala değiştirilebilir ve çalışma zamanında tahsis edilir.
David Schmitt

0

Winforms birleşik giriş kutusuna bağlandığımdan beri başka bir fikir:

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;

Ekran adından int değerini almak için gelen :

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");
    }
}

kullanımı:

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

-2

Neden olmasın:

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
Çünkü bu, belirtilen koşullarda mevcut alternatiflere göre oldukça pahalıdır.
David Schmitt

Ayrıca bu derlenmediği için
AustinWBryan

@AustinWBryan yes it
compiles
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.