Bộ khởi tạo trường không thể tham chiếu trường, phương thức hoặc thuộc tính không tĩnh


91

Tôi có một lớp học và khi tôi cố gắng sử dụng nó trong một lớp học khác, tôi nhận được lỗi bên dưới.

using System;
using System.Collections.Generic;
using System.Linq;

namespace MySite
{
    public class Reminders
    {
        public Dictionary<TimeSpan, string> TimeSpanText { get; set; }

        // We are setting the default values using the Costructor
        public Reminders()
        {
            TimeSpanText.Add(TimeSpan.Zero, "None");
            TimeSpanText.Add(new TimeSpan(0, 0, 5, 0), "5 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 15, 0), "15 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 30, 0), "30 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 1, 0, 0), "1 hour before");
            TimeSpanText.Add(new TimeSpan(0, 2, 0, 0), "2 hours before");
            TimeSpanText.Add(new TimeSpan(1, 0, 0, 0), "1 day before");
            TimeSpanText.Add(new TimeSpan(2, 0, 0, 0), "2 day before");
        }

    }
}

Sử dụng lớp trong lớp khác

class SomeOtherClass
{  
    private Reminders reminder = new Reminders();
    // error happens on this line:
    private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
    ....

Lỗi (CS0236):

A field initializer cannot reference the nonstatic field, method, or property

Tại sao nó xảy ra và làm thế nào để khắc phục nó?

Câu trả lời:


143

Đường thẳng này:

private dynamic defaultReminder = 
                          reminder.TimeSpanText[TimeSpan.FromMinutes(15)];

Bạn không thể sử dụng một biến cá thể để khởi tạo một biến cá thể khác. Tại sao? Bởi vì trình biên dịch có thể sắp xếp lại những thứ này - không có gì đảm bảo rằng remindersẽ được khởi tạo trước đó defaultReminder, vì vậy dòng trên có thể ném ra một NullReferenceException.

Thay vào đó, chỉ cần sử dụng:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Ngoài ra, hãy thiết lập giá trị trong hàm tạo:

private dynamic defaultReminder;

public Reminders()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
}

Có thêm chi tiết về lỗi trình biên dịch này trên MSDN - Lỗi trình biên dịch CS0236 .


3
Java dễ 'tha thứ' hơn cho những loại cấu trúc này. Không biết đó có phải là điều tốt không. stackoverflow.com/questions/1494735/…
— Wouter Schut

31
Không, trình biên dịch không thể sắp xếp lại các trình khởi tạo. Đặc tả ngôn ngữ C # nêu rõ, trong phần "10.5.5.2 Khởi tạo trường phiên bản", như sau: Các bộ khởi tạo biến được thực thi theo thứ tự văn bản mà chúng xuất hiện trong khai báo lớp. Điều này thậm chí được lặp lại trong "10.11.2 Bộ khởi tạo biến phiên bản" trong đó họ nói: Các bộ khởi tạo biến được thực thi theo thứ tự văn bản mà chúng xuất hiện trong khai báo lớp. Vì vậy, giải thích của bạn là sai. Đơn hàng đã được ấn định. Lý do tại sao nó không được phép là các nhà thiết kế của C # muốn nó theo cách đó.
— Jeppe Stig Nielsen

(Chỉ trong trường hợp một partial classvới "bộ phận" trong một số tác phẩm là thứ tự của các initializers lĩnh vực chưa rõ ràng, nhưng mà đi cho staticcác lĩnh vực cũng!)
— Jeppe Stig Nielsen

@WouterSchut Chủ đề bạn liên kết không phải về Java ?! Nó cũng là về C #, tuy nhiên với staticcác trường thay vì các trường cá thể.
— Jeppe Stig Nielsen

2
@Andrew Không đúng chút nào, Nhiều quyết định được đưa ra để cấm các hành vi xấu. mặc dù về mặt lý thuyết, chúng có thể được triển khai, một số được bảo vệ bởi Cảnh báo và một số là Lỗi rõ ràng. và tôi nghĩ rằng đây là một trong những trường hợp này ... mặc dù tiêu chuẩn nói rằng đó là thứ tự, ngay cả một nhà phát triển có kinh nghiệm cũng sẽ không tự tin nói điều đó (mà không cần tìm kiếm tiêu chuẩn).
— Tomer W

21

Bạn cần đặt mã đó vào hàm tạo của lớp của bạn:

private Reminders reminder = new Reminders();
private dynamic defaultReminder;

public YourClass()
{
    defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

Lý do là bạn không thể sử dụng một biến cá thể để khởi tạo một biến khác bằng bộ khởi tạo trường.


9

bạn có thể sử dụng như thế này

private dynamic defaultReminder => reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 

10
Chào mừng bạn đến với Stack Overflow! Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. Cũng vui lòng cố gắng không chèn mã của bạn bằng các nhận xét giải thích, vì điều này làm giảm khả năng đọc của cả mã và giải thích!
— jmattheis

3
Nó sử dụng => thay vì = do đó biến nó thành một thuộc tính.
— Vincent

2
Hãy cẩn thận khi sử dụng kỹ thuật này, vì việc sử dụng =>không đặt giá trị thực, nhưng sẽ thực thi mã mỗi khi defaultReminderđược truy cập. Điều này có thể không nhằm mục đích và có tác động tiêu cực đến hiệu suất hoặc tạo ra áp lực không mong muốn cho GC, v.v.
— Smilediver

5

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];là một bộ khởi tạo trường và thực thi đầu tiên (trước khi bất kỳ trường nào không có bộ khởi tạo được đặt thành giá trị mặc định của nó và trước khi phương thức khởi tạo phiên bản được gọi được thực thi). Các trường cá thể không có bộ khởi tạo sẽ chỉ có giá trị pháp lý (mặc định) sau khi tất cả các bộ khởi tạo trường phiên bản hoàn tất. Do thứ tự khởi tạo, các bộ khởi tạo cá thể được thực thi sau cùng, đó là lý do tại sao cá thể chưa được tạo ngay thời điểm các bộ khởi tạo được thực thi. Do đó trình biên dịch không thể cho phép bất kỳ thuộc tính cá thể (hoặc trường) nào được tham chiếu trước khi cá thể lớp được xây dựng hoàn chỉnh. Điều này là do bất kỳ quyền truy cập nào vào một biến thể hiện như remindertham chiếu ngầm đến instance ( this) để thông báo cho trình biên dịch biết vị trí bộ nhớ cụ thể của thể hiện sẽ sử dụng.

Đây cũng là lý do tại sao thiskhông được phép trong bộ khởi tạo trường cá thể.

Bộ khởi tạo biến cho một trường cá thể không thể tham chiếu đến cá thể đang được tạo. Do đó, đó là lỗi thời gian biên dịch khi tham chiếu điều này trong bộ khởi tạo biến, vì nó là lỗi thời gian biên dịch đối với bộ khởi tạo biến để tham chiếu bất kỳ thành viên cá thể nào thông qua simple_name .

Các thành viên kiểu duy nhất được đảm bảo sẽ được khởi tạo trước khi các trình khởi tạo trường cá thể được thực thi là các trình khởi tạo trường lớp (tĩnh) và các hàm tạo lớp (tĩnh) và các phương thức lớp. Vì các thành viên tĩnh là phiên bản độc lập, chúng có thể được tham chiếu bất cứ lúc nào:

class SomeOtherClass
{
  private static Reminders reminder = new Reminders();

  // This operation is allowed,
  // since the compiler can guarantee that the referenced class member is already initialized
  // when this instance field initializer executes
  private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
}

Đó là lý do tại sao các trình khởi tạo trường thể hiện chỉ được phép tham chiếu đến một thành viên lớp (thành viên tĩnh). Các quy tắc khởi tạo trình biên dịch này sẽ đảm bảo một kiểu khởi tạo xác định.

Để biết thêm chi tiết, tôi giới thiệu tài liệu này: Microsoft Docs: Khai báo lớp .

Điều này có nghĩa là một trường thể hiện tham chiếu đến thành viên thể hiện khác để khởi tạo giá trị của nó, phải được khởi tạo từ phương thức khởi tạo thể hiện hoặc thành viên được tham chiếu phải được khai báo static.

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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.