การสร้างพจนานุกรมคงที่ใน C #


128

วิธีใดที่มีประสิทธิภาพที่สุดในการสร้างการแม็ปค่าคงที่ (ไม่เปลี่ยนแปลงที่รันไทม์) ของstrings ถึงints คืออะไร

ฉันได้ลองใช้const Dictionaryแล้ว แต่ก็ไม่ได้ผล

ฉันสามารถใช้กระดาษห่อหุ้มที่ไม่เปลี่ยนรูปได้ด้วยความหมายที่เหมาะสม แต่ก็ยังดูไม่ถูกต้องทั้งหมด


สำหรับผู้ที่ถามฉันกำลังใช้IDataErrorInfoในคลาสที่สร้างขึ้นและกำลังมองหาวิธีที่จะทำให้การค้นหา columnName ในอาร์เรย์ของตัวอธิบายของฉัน

ฉันไม่รู้ (พิมพ์ผิดเมื่อทดสอบ! d'oh!) สวิตช์ที่ยอมรับสตริงนั่นคือสิ่งที่ฉันจะใช้ ขอบคุณ!


1
มีวิธีแก้ไขที่นี่: stackoverflow.com/questions/10066708/…

คำตอบ:


181

การสร้างพจนานุกรมค่าคงที่เวลาคอมไพล์อย่างแท้จริงที่สร้างขึ้นใน C # ไม่ใช่งานที่ตรงไปตรงมา อันที่จริงไม่มีคำตอบใดที่จะบรรลุสิ่งนั้นจริงๆ

มีทางออกหนึ่งที่ตรงกับความต้องการของคุณแม้ว่าจะไม่จำเป็นต้องเป็นวิธีที่ดี โปรดจำไว้ว่าตามข้อกำหนด C # ตารางสวิตช์เคสจะถูกคอมไพล์เป็นตารางกระโดดแฮชคงที่ นั่นคือเป็นพจนานุกรมคงที่ไม่ใช่ชุดคำสั่ง if-else ลองพิจารณาคำสั่ง switch-case ดังนี้:

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

นี่คือสิ่งที่คุณต้องการ และใช่ฉันรู้ว่ามันน่าเกลียด


9
มันสร้างรหัสอย่างไรก็ตามมีลักษณะการทำงานที่ดีสามารถคำนวณเวลาคอมไพล์และมีคุณสมบัติที่ดีอื่น ๆ สำหรับกรณีการใช้งานของฉันด้วย ฉันจะทำแบบนี้!
David Schmitt

4
โปรดระวังว่าการส่งคืนประเภทที่ไม่ใช่ค่าแม้เช่นนี้จะทำให้คลาสของคุณไม่เปลี่ยนรูป
Tom Anderson

35
switch-case นั้นยอดเยี่ยมมากจนคุณต้อง "foreach" ผ่านค่าต่างๆ
Alex

6
มันขาดฟังก์ชั่นที่ดีมากมายที่พจนานุกรมมี
Zinan Xing

1
@TomAnderson: โครงสร้างจะทำงานไม่เปลี่ยนรูปถ้ารายการที่ส่งคืนเป็นประเภทค่า (เปลี่ยนแปลงได้หรือไม่) หรือถ้าเป็นวัตถุคลาสที่ไม่เปลี่ยนรูป
supercat

37

มีคอลเล็กชันล้ำค่าที่ไม่เปลี่ยนรูปในกรอบปัจจุบัน ฉันนึกถึงตัวเลือกที่ค่อนข้างปราศจากความเจ็บปวดใน. NET 3.5:

ใช้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 () ทำงานได้ดีเพียงใด ฉันกลัวว่าจะมีเพียงผู้สร้างโปรไฟล์เท่านั้นที่ตอบคำถามนั้นได้
David Schmitt

10

นี่คือสิ่งที่ใกล้เคียงที่สุดที่คุณจะได้รับจาก "CONST Dictionary":

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

คอมไพเลอร์จะฉลาดพอที่จะสร้างโค้ดให้สะอาดที่สุด


8

ถ้าใช้ 4.5+ Framework ฉันจะใช้ 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 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
David Schmitt

1

ดูเหมือนจะไม่มีอินเทอร์เฟซที่ไม่เปลี่ยนรูปมาตรฐานใด ๆ สำหรับพจนานุกรมดังนั้นการสร้าง Wrapper จึงดูเหมือนเป็นทางเลือกเดียวที่สมเหตุสมผล

แก้ไข : Marc Gravell พบ ILookup ที่ฉันพลาดซึ่งจะช่วยให้คุณหลีกเลี่ยงการสร้าง Wrapper ใหม่ได้อย่างน้อยแม้ว่าคุณจะต้องเปลี่ยนพจนานุกรมด้วย. 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 }
        };

เนื่องจากเนื้อหาของพจนานุกรมยังคงไม่แน่นอนและมีการจัดสรรที่รันไทม์
David Schmitt

0

เป็นอีกหนึ่งความคิดเนื่องจากฉันผูกพันกับ winforms combobox:

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

ด้วยเพราะสิ่งนี้ไม่ได้รวบรวม
AustinWBryan

@AustinWBryan ใช่มันรวบรวม
derHugo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.