Khai báo một mảng const


458

Có thể viết một cái gì đó tương tự như sau?

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

static có thể được sử dụng, chuỗi tĩnh công khai [] Titles = new string [] {"German", "Spain"};
Ray

Câu trả lời:


691

Có, nhưng bạn cần khai báo readonlythay vì const:

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

Lý do là constchỉ có thể được áp dụng cho một trường có giá trị được biết tại thời điểm biên dịch. Trình khởi tạo mảng bạn đã hiển thị không phải là biểu thức không đổi trong C #, do đó, nó tạo ra lỗi trình biên dịch.

Khai báo nó readonlygiải quyết vấn đề đó bởi vì giá trị không được khởi tạo cho đến thời gian chạy (mặc dù nó được đảm bảo đã khởi tạo trước khi sử dụng mảng lần đầu tiên).

Tùy thuộc vào những gì cuối cùng bạn muốn đạt được, bạn cũng có thể xem xét khai báo một enum:

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

115
Lưu ý rằng mảng ở đây không chỉ đọc, tất nhiên; Tiêu đề [2] = "Tiếng Wales"; sẽ hoạt động tốt trong thời gian chạy
Marc Gravell

46
Bạn có thể muốn nó tĩnh quá
tymtam

4
Làm thế nào về việc khai báo một mảng "const" trong một thân phương thức, không phải trong lớp một?
serhio

19
Xin lỗi vì đã bỏ phiếu, nhưng const cũng ngụ ý tĩnh. Khai báo mảng như chỉ đọc không gần với cách giải quyết. nó cần phải readonly staticcó bất kỳ sự tương đồng với ngữ nghĩa được yêu cầu.
Anton

3
@Anton, bạn và "người theo dõi" của bạn đã gỡ bỏ downvote chưa? Đối với tôi statickhông bắt buộc phải làm cho nó hoạt động, nó chỉ thêm khả năng tham chiếu Titlesmà không có cá thể, nhưng loại bỏ khả năng thay đổi giá trị cho các trường hợp khác nhau (ví dụ: bạn có thể có hàm tạo với tham số tùy thuộc vào việc bạn thay đổi giá trị của readonlytrường đó ).
Sinatr

57

Bạn có thể khai báo mảng là readonly, nhưng hãy nhớ rằng bạn có thể thay đổi thành phần của readonlymảng.

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

Cân nhắc sử dụng enum, như Cody đề xuất hoặc IList.

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

31
Trong .NET 4.5 trở lên, bạn có thể khai báo danh sách là IReadOnlyList <string> thay vì IList <string>.
Grzegorz Smulko

Để rõ ràng, bạn vẫn có thể thay đổi các giá trị trong IReadOnlyList (chỉ không thêm hoặc xóa các phần tử). Nhưng vâng, tuyên bố nó là IReadOnlyList sẽ tốt hơn IList.
KevinVictor

Câu hỏi là về constKHÔNG readonly...
Yousha Aleayoub

51

Bạn không thể tạo mảng 'const' vì mảng là đối tượng và chỉ có thể được tạo khi chạy và các thực thể const được giải quyết tại thời điểm biên dịch.

Thay vào đó, những gì bạn có thể làm là khai báo mảng của bạn là "chỉ đọc". Điều này có tác dụng tương tự như const ngoại trừ giá trị có thể được đặt trong thời gian chạy. Nó chỉ có thể được đặt một lần và sau đó là giá trị chỉ đọc (tức là const).


2
Bài đăng này nhấn mạnh lý do tại sao mảng không thể được khai báo là hằng số
Radderz

1
Có thể khai báo một mảng không đổi; vấn đề là khởi tạo nó với một giá trị không đổi. Ví dụ hoạt động duy nhất xuất hiện const int[] a = null;trong đầu là không hữu ích lắm, nhưng thực sự là một thể hiện của hằng số mảng.
waldrumpus

Đây là câu trả lời đúng duy nhất.
Yousha Aleayoub

17

Vì C # 6, bạn có thể viết nó như sau:

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

Xem thêm: C #: C # 6.0 mới và được cải tiến (cụ thể là chương "Các thuộc tính và chức năng cơ thể biểu hiện")

Điều này sẽ tạo một thuộc tính tĩnh chỉ đọc, nhưng nó vẫn sẽ cho phép bạn thay đổi nội dung của mảng được trả về, nhưng khi bạn gọi lại thuộc tính đó, bạn sẽ nhận được mảng ban đầu, không thay đổi.

Để làm rõ, mã này giống như (hoặc thực tế là một tốc ký cho):

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

Xin lưu ý rằng có một nhược điểm của phương pháp này: Một mảng mới thực sự được khởi tạo trên mỗi tham chiếu, vì vậy nếu bạn đang sử dụng một mảng rất lớn, đây có thể không phải là giải pháp hiệu quả nhất. Nhưng nếu bạn sử dụng lại cùng một mảng (bằng cách đặt nó vào một thuộc tính riêng), nó sẽ lại mở ra khả năng thay đổi nội dung của mảng.

Nếu bạn muốn có một mảng (hoặc danh sách) bất biến, bạn cũng có thể sử dụng:

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

Nhưng, điều này vẫn có rủi ro cho các thay đổi, vì bạn vẫn có thể chuyển nó trở lại chuỗi [] và thay đổi nội dung, như sau:

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

1
Lợi nhuận của việc sử dụng tài sản thay vì lĩnh vực trong trường hợp này là gì?
nicolay.anykienko

2
Một trường không thể trả về một đối tượng mới trên mỗi cuộc gọi. Một tài sản về cơ bản là một loại "chức năng ngụy trang".
mjepson

1
Nếu bạn đang đề cập đến tùy chọn cuối cùng, điều đó có thể được thực hiện bằng cách sử dụng cả trường hoặc thuộc tính, nhưng vì nó là công khai, tôi thích một Thuộc tính. Tôi không bao giờ sử dụng một lĩnh vực công cộng kể từ khi giới thiệu Thuộc tính.
mjepson

1
Có một nhược điểm khác của cách tiếp cận thứ 1: chẳng hạn, bạn sẽ không gặp phải lỗi biên dịch nếu bạn gán Titles[0]cho - chẳng hạn, thực tế, nỗ lực chuyển nhượng bị bỏ qua lặng lẽ. Kết hợp với việc không hiệu quả trong việc tạo lại mảng mỗi lần, tôi tự hỏi liệu cách tiếp cận này có đáng để hiển thị hay không. Ngược lại, cách tiếp cận thứ 2 là hiệu quả, và bạn phải đi ra ngoài để đánh bại sự bất biến.
mkuity0

6

Nếu bạn khai báo một mảng phía sau giao diện IReadOnlyList, bạn sẽ nhận được một mảng không đổi với các giá trị không đổi được khai báo khi chạy:

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

Có sẵn trong .NET 4.5 trở lên.


6

Một giải pháp .NET Framework v4.5 + cải thiện câu trả lời của tdbeckett :

using System.Collections.ObjectModel;

// ...

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

Lưu ý: Cho rằng bộ sưu tập là hằng số về mặt khái niệm, có thể có ý nghĩa để làm cho nó statickhai báo nó ở cấp độ lớp .

Ở trên:

  • Khởi tạo trường sao lưu ngầm của thuộc tính một lần với mảng.

    • Lưu ý rằng { get; }- tức là, chỉ khai báo một getter thuộc tính - là điều làm cho chính thuộc tính đó hoàn toàn chỉ đọc (cố gắng kết hợp readonlyvới { get; }thực sự là một lỗi cú pháp).

    • Ngoài ra, bạn chỉ có thể bỏ qua { get; }và thêm readonlyđể tạo một trường thay vì thuộc tính, như trong câu hỏi, nhưng để lộ các thành viên dữ liệu công khai dưới dạng các thuộc tính thay vì các trường là một thói quen tốt để hình thành.

  • Tạo cấu trúc giống như mảng (cho phép truy cập được lập chỉ mục ) thực sự chỉ đọc và mạnh mẽ (không đổi về mặt khái niệm, một khi được tạo), cả hai đều liên quan đến:

    • ngăn chặn sửa đổi của bộ sưu tập như một toàn thể (chẳng hạn như bằng cách loại bỏ hoặc thêm các yếu tố, hoặc bằng cách gán cho một bộ sưu tập mới để biến).
    • ngăn chặn sửa đổi các yếu tố cá nhân .
      (Ngay cả gián tiếp thay đổi là không thể - không giống như với một IReadOnlyList<T>giải pháp, mà một (string[])diễn viên có thể được sử dụng để đạt được ghi vào các yếu tố , như trong câu trả lời hữu ích mjepsen của .
      Lỗ hổng này cũng áp dụng cho các IReadOnlyCollection<T> giao diện , trong đó, bất chấp sự giống nhau trong tên đến lớp ReadOnlyCollection , thậm chí không hỗ trợ truy cập được lập chỉ mục , về cơ bản không phù hợp để cung cấp quyền truy cập giống như mảng.)

1
@mortb: Thật không may, IReadOnlyCollectionkhông hỗ trợ truy cập được lập chỉ mục, vì vậy nó không thể được sử dụng ở đây. Ngoài ra, giống như IReadOnlyList(có quyền truy cập được lập chỉ mục), nó dễ bị thao túng phần tử bằng cách chuyển trở lại string[]. Nói cách khác: ReadOnlyCollection(mà bạn không thể truyền một chuỗi chuỗi thành) là giải pháp mạnh mẽ nhất. Không sử dụng getter là một tùy chọn (và tôi đã cập nhật câu trả lời để lưu ý điều đó), nhưng với dữ liệu công khai , có lẽ tốt hơn để gắn bó với một tài sản.
mkuity0

5

Đối với nhu cầu của tôi, tôi xác định staticmảng, thay vì không thể constvà nó hoạt động: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };


1
Chỉ cần xóa constkhỏi ví dụ OP cũng hoạt động, nhưng điều đó (hoặc câu trả lời của bạn) cho phép thay đổi cả: Titlesthể hiện và bất kỳ giá trị nào. Vì vậy, điểm trong câu trả lời này là gì?
Sinatr

@Sinatr, tôi đã trả lời điều này 3 năm trước, khi tôi sải bước làm việc trong C #. Tôi đã bỏ nó, bây giờ tôi đang ở trong thế giới Java. Có lẽ tôi đã quên thêmreadonly
ALZ

Sau khi suy nghĩ thứ hai, câu trả lời của bạn là cách trực tiếp làm cho mã OP hoạt động , không có bất kỳ const/ readonlycân nhắc nào, chỉ đơn giản là làm cho nó hoạt động (như nếu constlà một lỗi cú pháp). Đối với một số người, nó dường như là một câu trả lời có giá trị (có lẽ họ cũng đã cố gắng sử dụng constdo nhầm lẫn?).
Sinatr

5

Bạn có thể thực hiện một cách tiếp cận khác: xác định một chuỗi không đổi để biểu diễn mảng của bạn và sau đó chia chuỗi thành một mảng khi bạn cần, ví dụ:

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

Cách tiếp cận này cung cấp cho bạn một hằng số có thể được lưu trữ trong cấu hình và được chuyển đổi thành một mảng khi cần thiết.


8
Tôi nghĩ rằng chi phí thực hiện việc phân chia vượt xa bất kỳ lợi ích nào được thực hiện bằng cách xác định const. Nhưng +1 cho một cách tiếp cận độc đáo và suy nghĩ bên ngoài hộp! ;)
Radderz

Tôi chuẩn bị đăng cùng một giải pháp và sau đó thấy điều này, trái với những nhận xét gay gắt và tiêu cực, điều này thực sự hoàn hảo cho kịch bản của tôi, nơi tôi cần chuyển một const cho một Thuộc tính, và sau đó tôi chia giá trị trong hàm tạo thuộc tính cho có được những gì tôi cần. và tôi thấy không có lý do tại sao nó sẽ có chi phí hiệu năng vì các thuộc tính không được tạo cho mỗi phiên bản.
Kalpesh Popat


3

Đây là một cách để làm những gì bạn muốn:

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

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

Nó rất giống với việc thực hiện một mảng chỉ đọc.


8
Bạn chỉ có thể làm điều đó như public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; không cần phải tạo lại danh sách trên mỗi lần truy xuất nếu bạn đặt nó thành ReadOnlyCollection.
Nyerguds

3

Đây là câu trả lời đúng duy nhất. Bạn hiện không thể làm điều này.

Tất cả các câu trả lời khác đang đề xuất sử dụng các biến chỉ đọc tĩnh tương tự , nhưng không giống với hằng số. Một hằng số được mã hóa cứng vào lắp ráp. Một biến chỉ đọc tĩnh có thể thiết lập được một lần, có thể là một đối tượng được khởi tạo.

Chúng đôi khi có thể thay thế cho nhau, nhưng không phải lúc nào cũng vậy.


2

Tôi tin rằng bạn chỉ có thể làm cho nó chỉ đọc.


2

Mảng có lẽ là một trong những thứ chỉ có thể được đánh giá khi chạy. Các hằng số phải được đánh giá tại thời điểm biên dịch. Hãy thử sử dụng "chỉ đọc" thay vì "const".


0

Thay vào đó, để giải quyết vấn đề có thể sửa đổi thành phần với mảng chỉ đọc, bạn có thể sử dụng thuộc tính tĩnh thay thế. (Các phần tử riêng lẻ vẫn có thể được thay đổi, nhưng những thay đổi này sẽ chỉ được thực hiện trên bản sao cục bộ của mảng.)

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

Tất nhiên, điều này sẽ không đặc biệt hiệu quả vì một mảng chuỗi mới được tạo ra mỗi lần.


0

Thay thế tốt nhất:

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 };
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.