ประกาศอาร์เรย์ const


458

เป็นไปได้ไหมที่จะเขียนบางสิ่งที่คล้ายกับข้อความต่อไปนี้?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

สามารถใช้แบบคงที่สตริงคงที่สาธารณะ [] ชื่อ = สตริงใหม่ [] {"เยอรมัน", "สเปน"};
เรย์

คำตอบ:


691

ใช่ แต่คุณต้องประกาศreadonlyแทนconst:

public static readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

เหตุผลคือconstสามารถนำไปใช้กับเขตข้อมูลที่มีค่าเป็นที่รู้จักในเวลารวบรวม เครื่องมือเตรียมใช้งานอาร์เรย์ที่คุณแสดงไม่ใช่นิพจน์คงที่ใน C # ดังนั้นจึงเกิดข้อผิดพลาดของคอมไพเลอร์

การประกาศว่าจะreadonlyแก้ปัญหาได้เนื่องจากค่าไม่ได้เริ่มต้นจนถึงเวลาใช้งาน (แม้ว่าจะรับประกันว่าจะได้รับการเริ่มต้นก่อนที่จะใช้อาร์เรย์ครั้งแรก)

ขึ้นอยู่กับว่าคุณต้องการบรรลุเป้าหมายในท้ายที่สุดคุณอาจพิจารณาประกาศ enum:

public enum Titles { German, Spanish, Corrects, Wrongs };

115
โปรดทราบว่าอาร์เรย์ที่นี่ไม่ได้อ่านอย่างเดียวแน่นอน ชื่อเรื่อง [2] = "เวลส์"; จะทำงานได้ดีที่รันไทม์
Marc Gravell

46
คุณอาจต้องการให้มันคงที่เช่นกัน
tymtam

4
วิธีการเกี่ยวกับการประกาศอาร์เรย์ "const" ในร่างกายวิธีการไม่ได้อยู่ในชั้นเรียนหรือไม่
serhio

19
ขออภัยสำหรับการลงคะแนนเสียง แต่ const ยังหมายถึงคงที่ การประกาศอาร์เรย์เป็นแบบอ่านอย่างเดียวไม่ได้ใกล้เคียงกับวิธีแก้ปัญหา มันจะต้องreadonly staticมีความคล้ายคลึงกันของความหมายที่ร้องขอใด ๆ
Anton

3
@Anton คุณและผู้ติดตาม "ของคุณได้ลบ downvote หรือไม่ สำหรับผมstaticไม่จำเป็นต้องที่จะทำให้มันทำงานก็เพียงแค่เพิ่มความเป็นไปได้ที่จะอ้างอิงTitlesได้โดยไม่ต้องยกตัวอย่างเช่น แต่ลบไปได้ที่จะคุ้มค่าการเปลี่ยนแปลงสำหรับอินสแตนซ์ที่แตกต่างกัน (เช่นคุณสามารถมีคอนสตรัคกับพารามิเตอร์ซึ่งขึ้นอยู่กับคุณเปลี่ยนค่าของreadonlyฟิลด์)
Sinatr

57

คุณสามารถประกาศอาร์เรย์เป็นreadonlyแต่โปรดจำไว้ว่าคุณสามารถเปลี่ยนองค์ประกอบของreadonlyอาร์เรย์

public readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
...
Titles[0] = "bla";

พิจารณาใช้ enum ตามที่ Cody แนะนำหรือ IList

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();

31
ใน. NET 4.5 และสูงกว่าคุณสามารถประกาศรายการเป็น IReadOnlyList <string> แทน IList <string>
Grzegorz Smulko

เพื่อให้ชัดเจนคุณยังสามารถเปลี่ยนค่าใน IReadOnlyList (เพียงแค่ไม่เพิ่มหรือลบองค์ประกอบ) แต่ใช่การประกาศว่าเป็น IReadOnlyList จะดีกว่า IList
KevinVictor

คำถามเกี่ยวกับconstไม่ใช่readonly...
Yousha Aleayoub

51

คุณไม่สามารถสร้างอาร์เรย์ 'const' ได้เนื่องจากอาร์เรย์เป็นวัตถุและสามารถสร้างได้ในขณะใช้งานจริงเท่านั้น

คุณสามารถทำอะไรแทนการประกาศอาร์เรย์ของคุณเป็น "อ่านอย่างเดียว" สิ่งนี้มีผลเช่นเดียวกับ const ยกเว้นค่าสามารถตั้งค่าได้ที่รันไทม์ สามารถตั้งค่าได้เพียงครั้งเดียวและหลังจากนั้นจะมีค่าแบบอ่านอย่างเดียว (เช่น const)


2
โพสต์นี้เน้นว่าทำไมอาร์เรย์ไม่สามารถประกาศเป็นค่าคงที่ได้
Radderz

1
มันเป็นไปได้ที่จะประกาศอาร์เรย์คงที่ ปัญหาคือการเริ่มต้นกับค่าคงที่ ตัวอย่างการทำงานเดียวที่นึกได้const int[] a = null;คือไม่มีประโยชน์มาก แต่เป็นตัวอย่างของค่าคงที่ของอาร์เรย์
waldrumpus

นี่เป็นคำตอบเดียวที่ถูกต้อง
Yousha Aleayoub

17

ตั้งแต่ C # 6 คุณสามารถเขียนได้เช่น:

public static string[] Titles => new string[] { "German", "Spanish", "Corrects", "Wrongs" };

ดูเพิ่มเติมที่: C #: C # 6.0 ที่ปรับปรุงใหม่ (เฉพาะตอน "ฟังก์ชั่นและคุณสมบัติของ Expression Bodied")

สิ่งนี้จะทำให้คุณสมบัติสแตติกแบบอ่านอย่างเดียว แต่ยังคงช่วยให้คุณสามารถเปลี่ยนเนื้อหาของอาเรย์ที่ส่งคืน แต่เมื่อคุณเรียกคุณสมบัติอีกครั้งคุณจะได้รับอาเรย์ดั้งเดิมที่ไม่เปลี่ยนแปลงอีกครั้ง

สำหรับการชี้แจงรหัสนี้เป็นเช่นเดียวกับ (หรือจริงชวเลขสำหรับ):

public static string[] Titles
{
    get { return new string[] { "German", "Spanish", "Corrects", "Wrongs" }; }
}

โปรดทราบว่ามีข้อเสียของวิธีการนี้: อาร์เรย์ใหม่นั้นมีการสร้างอินสแตนซ์สำหรับแต่ละการอ้างอิงดังนั้นถ้าคุณใช้อาร์เรย์ที่มีขนาดใหญ่มากนี่อาจไม่ใช่วิธีที่มีประสิทธิภาพที่สุด แต่ถ้าคุณใช้อาร์เรย์เดิมอีกครั้ง (โดยใส่ไว้ในแอตทริบิวต์ส่วนตัวเช่น) มันจะเปิดโอกาสในการเปลี่ยนเนื้อหาของอาร์เรย์อีกครั้ง

หากคุณต้องการมีอาร์เรย์ (หรือรายการ) ที่ไม่เปลี่ยนรูปแบบคุณสามารถใช้:

public static IReadOnlyList<string> Titles { get; } = new string[] { "German", "Spanish", "Corrects", "Wrongs" };

แต่สิ่งนี้ยังมีความเสี่ยงต่อการเปลี่ยนแปลงเนื่องจากคุณยังสามารถส่งกลับไปยังสตริง [] และแก้ไขเนื้อหาได้เช่น:

((string[]) Titles)[1] = "French";

1
กำไรจากการใช้คุณสมบัติแทนฟิลด์ในกรณีนี้คืออะไร?
nicolay.anykienko

2
เขตข้อมูลไม่สามารถส่งคืนวัตถุใหม่ในการโทรแต่ละครั้ง คุณสมบัตินั้นเป็นประเภทของ "ฟังก์ชันปลอมตัว"
mjepson

1
หากคุณอ้างถึงตัวเลือกสุดท้ายที่สามารถทำได้โดยใช้ทั้งฟิลด์หรือพร็อพเพอร์ตี้ แต่เนื่องจากเป็นสาธารณะฉันชอบพร็อพเพอร์ตี้ ฉันไม่เคยใช้ฟิลด์สาธารณะตั้งแต่เปิดตัวคุณสมบัติ
mjepson

1
มีข้อเสียอีกประการหนึ่งสำหรับวิธีที่ 1: คุณจะไม่ได้รับข้อผิดพลาดในการคอมไพล์หากคุณกำหนดTitles[0]ตัวอย่างเช่น - ในกรณีความพยายามในการมอบหมายนั้นถูกเพิกเฉยอย่างเงียบ ๆ เมื่อรวมกับความไร้ประสิทธิภาพของการสร้างอาร์เรย์ใหม่ทุกครั้งฉันสงสัยว่าวิธีการนี้น่าจะคุ้มค่าหรือไม่ ในทางตรงกันข้ามวิธีที่ 2 นั้นมีประสิทธิภาพและคุณต้องออกไปเพื่อเอาชนะความไม่สามารถเปลี่ยนแปลงได้
mklement0

6

หากคุณประกาศอาร์เรย์ที่อยู่เบื้องหลังส่วนต่อประสาน IReadOnlyList คุณจะได้รับอาร์เรย์คงที่พร้อมค่าคงที่ที่ประกาศ ณ รันไทม์:

public readonly IReadOnlyList<string> Titles = new [] {"German", "Spanish", "Corrects", "Wrongs" };

พร้อมใช้งานใน. NET 4.5 และสูงกว่า


6

.NET Framework v4.5 + วิธีการแก้ปัญหาที่ช่วยเพิ่มในคำตอบของ tdbeckett :

using System.Collections.ObjectModel;

// ...

public ReadOnlyCollection<string> Titles { get; } = new ReadOnlyCollection<string>(
  new string[] { "German", "Spanish", "Corrects", "Wrongs" }
);

หมายเหตุ: ระบุว่าคอลเลกชันจะคงแนวคิดก็อาจทำให้ความรู้สึกที่จะทำให้มันstaticที่จะประกาศได้ในระดับระดับ

ข้างบน:

  • เตรียมข้อมูลเบื้องต้นให้กับเขตข้อมูลสำรองโดยนัยของคุณสมบัติหนึ่งครั้งด้วยอาร์เรย์

    • หมายเหตุ{ get; }- คือประกาศเพียงคุณสมบัติทะเยอทะยาน - เป็นสิ่งที่ทำให้คุณสมบัติตัวเองโดยปริยายอ่านอย่างเดียว (พยายามที่จะรวมreadonlyกับ{ get; }เป็นจริงไวยากรณ์ผิดพลาด)

    • หรือคุณสามารถละเว้น { get; }และเพิ่มreadonlyเพื่อสร้างเขตข้อมูลแทนคุณสมบัติเช่นเดียวกับคำถาม แต่การเปิดเผยให้สมาชิกข้อมูลสาธารณะเป็นคุณสมบัติแทนที่จะเป็นเขตข้อมูลเป็นนิสัยที่ดีในการสร้าง

  • สร้างอาร์เรย์เหมือนโครงสร้าง (ให้เข้าถึงการจัดทำดัชนี ) ที่เป็นจริงและทนทานอ่านอย่างเดียว (แนวคิดคงสร้างครั้งเดียว), ความเคารพทั้งที่มี:

    • ป้องกันการแก้ไขคอลเลกชันโดยรวม (เช่นโดยการลบหรือเพิ่มองค์ประกอบหรือโดยการกำหนดคอลเลกชันใหม่ให้กับตัวแปร)
    • ป้องกันการเปลี่ยนแปลงของแต่ละองค์ประกอบ
      (แม้ทางอ้อมการปรับเปลี่ยนเป็นไปไม่ได้ - แตกต่างกับIReadOnlyList<T>วิธีการแก้ปัญหาที่หล่อสามารถนำมาใช้เพื่อให้เข้าถึงการเขียนไปยังองค์ประกอบดังแสดงในคำตอบที่เป็นประโยชน์ mjepsen ของ . ช่องโหว่เช่นเดียวกับอินเตอร์เฟซซึ่งแม้จะมีความคล้ายคลึงกันในชื่อ เพื่อชั้น , ไม่ได้สนับสนุนการจัดทำดัชนีการเข้าถึงทำให้มันเป็นพื้นฐานที่ไม่เหมาะสมสำหรับการให้อาร์เรย์เช่นการเข้าถึง.)(string[])
      IReadOnlyCollection<T> ReadOnlyCollection

1
@mortb: น่าเสียดายที่IReadOnlyCollectionไม่รองรับการเข้าถึงที่จัดทำดัชนีดังนั้นจึงไม่สามารถใช้ที่นี่ได้ นอกจากนี้เช่นIReadOnlyList(ซึ่งจะมีการจัดทำดัชนีการเข้าถึง) string[]มันเป็นความเสี่ยงที่จะจัดการองค์ประกอบโดยการหล่อกลับไป ในคำอื่น ๆ : ReadOnlyCollection(ซึ่งคุณไม่สามารถทอดอาเรย์สตริงไป) เป็นทางออกที่แข็งแกร่งที่สุด การไม่ใช้ getter เป็นตัวเลือก (และฉันได้อัปเดตคำตอบเพื่อให้ทราบแล้ว) แต่ด้วยข้อมูลสาธารณะน่าจะดีกว่าถ้าใช้คุณสมบัติ
mklement0

5

สำหรับความต้องการของฉันฉันกำหนดstaticอาร์เรย์แทนเป็นไปไม่ได้constและใช้งานได้: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };


1
เพียงแค่ลบออกconstจากตัวอย่าง OP แล้วยังใช้งานได้ แต่นั่น (หรือคำตอบของคุณ) อนุญาตให้เปลี่ยนทั้ง: Titlesอินสแตนซ์และค่าใด ๆ ดังนั้นคำตอบในประเด็นนี้คืออะไร?
Sinatr

@Sinatr ฉันตอบเมื่อ 3 ปีก่อนเมื่อฉันทำงานใน C # ฉันทิ้งไว้ตอนนี้ฉันอยู่ในโลก Java บางทีฉันลืมที่จะเพิ่มreadonly
ALZ

หลังจากที่คิดว่าสองคำตอบของคุณโดยตรงวิธีที่จะทำให้การทำงานรหัส OPโดยไม่ได้const/ readonlyการพิจารณาเพียงแค่ทำให้มันทำงาน (เช่นถ้าconstเป็นความผิดพลาดทางไวยากรณ์) สำหรับบางคนดูเหมือนว่าจะเป็นคำตอบที่มีค่า (บางทีพวกเขาอาจลองใช้constโดยไม่ได้ตั้งใจด้วย)
Sinatr

5

คุณสามารถใช้แนวทางที่แตกต่าง: กำหนดสตริงคงที่เพื่อเป็นตัวแทนอาร์เรย์ของคุณแล้วแยกสตริงเป็นอาร์เรย์เมื่อคุณต้องการเช่น

const string DefaultDistances = "5,10,15,20,25,30,40,50";
public static readonly string[] distances = DefaultDistances.Split(',');

วิธีนี้ช่วยให้คุณมีค่าคงที่ซึ่งสามารถเก็บไว้ในการกำหนดค่าและแปลงเป็นอาร์เรย์เมื่อจำเป็น


8
ฉันคิดว่าค่าใช้จ่ายในการดำเนินการแยกไกลเกินกว่าผลประโยชน์ใด ๆ ที่ทำโดยการกำหนด const แต่ +1 สำหรับวิธีการที่ไม่เหมือนใครและคิดนอกกรอบ! ;)
Radderz

ฉันกำลังจะโพสต์คำตอบเดียวกันแล้วก็เห็นสิ่งนี้ตรงกันข้ามกับคำพูดที่รุนแรงและเชิงลบนี่เป็นจริงที่สมบูรณ์แบบสำหรับสถานการณ์ของฉันที่ฉันต้องส่ง const ไปยังแอตทริบิวต์และจากนั้นฉันแบ่งค่าในตัวสร้างคุณลักษณะเพื่อ รับสิ่งที่ฉันต้องการ และฉันไม่เห็นเหตุผลว่าทำไมมันจะมีต้นทุนประสิทธิภาพเนื่องจากไม่ได้สร้างแอตทริบิวต์สำหรับแต่ละอินสแตนซ์
Kalpesh Popat

4

เพื่อความสมบูรณ์ขณะนี้เรายังมี ImmutableArays ในการกำจัดของเรา สิ่งนี้ควรจะไม่เปลี่ยนรูปอย่างแท้จริง:

public readonly static ImmutableArray<string> Tiles = ImmutableArray.Create(new[] { "German", "Spanish", "Corrects", "Wrongs" });

ต้องใช้ System.Collections การอ้างอิง NuGet ที่ไม่น่าเชื่อ

https://msdn.microsoft.com/en-us/library/mt452182(v=vs.111).aspx


3

นี่คือวิธีการทำสิ่งที่คุณต้องการ:

using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

public ReadOnlyCollection<string> Titles { get { return new List<string> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();}}

มันคล้ายกันมากกับการทำอาเรย์แบบอ่านอย่างเดียว


8
คุณสามารถทำสิ่งนั้นตามpublic static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; ไม่จำเป็นต้องสร้างรายการใหม่ทุกครั้งที่ดึงข้อมูลหากคุณกำหนดให้เป็น ReadOnlyCollection
Nyerguds

3

นี่เป็นคำตอบเดียวที่ถูกต้อง คุณไม่สามารถทำได้ในขณะนี้

คำตอบอื่น ๆ ทั้งหมดแนะนำให้ใช้ตัวแปรแบบอ่านอย่างเดียวแบบสแตติกซึ่งคล้ายกับแต่ไม่เหมือนกับค่าคงที่ ค่าคงที่จะถูกเข้ารหัสอย่างหนักในชุดประกอบ ตัวแปรแบบอ่านอย่างเดียวแบบสแตติกสามารถตั้งค่าได้หนึ่งครั้งอาจเป็นเพราะวัตถุถูกเริ่มต้น

บางครั้งสิ่งเหล่านี้สามารถใช้แทนกันได้ แต่ไม่เสมอไป



2

อาเรย์อาจเป็นหนึ่งในสิ่งเหล่านั้นที่สามารถประเมินได้ที่รันไทม์เท่านั้น ค่าคงที่จะต้องประเมินในเวลารวบรวม ลองใช้ "อ่านอย่างเดียว" แทน "const"


0

เพื่อเป็นทางเลือกในการแก้ไขปัญหาองค์ประกอบที่สามารถแก้ไขได้ด้วยอาเรย์แบบอ่านอย่างเดียวคุณสามารถใช้คุณสมบัติคงที่แทน (องค์ประกอบแต่ละอย่างยังคงสามารถเปลี่ยนแปลงได้ แต่การเปลี่ยนแปลงเหล่านี้จะทำในสำเนาภายในของอาร์เรย์เท่านั้น)

public static string[] Titles 
{
    get
    {
        return new string[] { "German", "Spanish", "Corrects", "Wrongs"};
    }
}

แน่นอนว่าสิ่งนี้จะไม่มีประสิทธิภาพโดยเฉพาะอย่างยิ่งเนื่องจากมีการสร้างอาร์เรย์สตริงใหม่ทุกครั้ง


0

ทางเลือกที่ดีที่สุด:

public static readonly byte[] ZeroHash = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.