ตัวเริ่มต้นฟิลด์ไม่สามารถอ้างอิงฟิลด์วิธีการหรือคุณสมบัติที่ไม่คงที่


96

ฉันมีชั้นเรียนและเมื่อฉันพยายามใช้ในชั้นเรียนอื่นฉันได้รับข้อผิดพลาดด้านล่าง

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

    }
}

การใช้คลาสในคลาสอื่น

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

ข้อผิดพลาด (CS0236):

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

ทำไมมันถึงเกิดขึ้นและจะแก้ไขได้อย่างไร?

คำตอบ:


147

บรรทัดนี้:

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

คุณไม่สามารถใช้ตัวแปรอินสแตนซ์เพื่อเตรียมใช้งานตัวแปรอินสแตนซ์อื่น ทำไม? เนื่องจากคอมไพลเลอร์สามารถจัดเรียงสิ่งเหล่านี้ใหม่ - ไม่มีการรับประกันว่าreminderจะเริ่มต้นก่อนdefaultReminderดังนั้นบรรทัดด้านบนอาจส่งไฟล์NullReferenceException.

ให้ใช้:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

หรือตั้งค่าในตัวสร้าง:

private dynamic defaultReminder;

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

- มีรายละเอียดเพิ่มเติมเกี่ยวกับการรวบรวมข้อผิดพลาดนี้ใน MSDN เป็นคอมไพเลอร์ข้อผิดพลาด CS0236


3
Java นั้น 'ให้อภัย' มากกว่าสำหรับโครงสร้างประเภทนี้ ไม่รู้ว่านั่นเป็นเรื่องดีหรือเปล่า stackoverflow.com/questions/1494735/…
Wouter Schut

32
ไม่คอมไพลเลอร์ไม่สามารถจัดเรียงตัวเริ่มต้นใหม่ได้ สถานะข้อกำหนดภาษา C # ภายใต้ส่วน "10.5.5.2 การเริ่มต้นฟิลด์อินสแตนซ์" ต่อไปนี้: ตัวเริ่มต้นตัวแปรจะดำเนินการตามลำดับข้อความที่ปรากฏในการประกาศคลาส สิ่งนี้ซ้ำแล้วซ้ำอีกใน "ตัวเริ่มต้นตัวแปรอินสแตนซ์ 10.11.2" ที่พวกเขากล่าวว่า: ตัวเริ่มต้นตัวแปรจะดำเนินการตามลำดับข้อความที่ปรากฏในการประกาศคลาส ดังนั้นคำอธิบายของคุณผิด คำสั่งได้รับการแก้ไข สาเหตุที่ไม่อนุญาตก็คือผู้ออกแบบของ C # ต้องการให้เป็นเช่นนั้น
Jeppe Stig Nielsen

(เฉพาะในกรณีของ a ที่partial classมี "parts" ในหลายไฟล์ลำดับของตัวเริ่มต้นฟิลด์ไม่ชัดเจน แต่ก็เป็นไปตามstaticช่องด้วย!)
Jeppe Stig Nielsen

@WouterSchut เธรดที่คุณลิงค์ไม่เกี่ยวกับ Java?! เป็นเรื่องเกี่ยวกับ C # เช่นกันอย่างไรก็ตามมีstaticฟิลด์แทนฟิลด์อินสแตนซ์
Jeppe Stig Nielsen

2
@ แอนดรูว์ไม่เป็นความจริงเลยมีการตัดสินใจหลายอย่างเพื่อห้ามการปฏิบัติที่ไม่ดี แม้ว่าในทางทฤษฎีสามารถนำไปปฏิบัติได้บางส่วนได้รับการปกป้องโดยคำเตือนและบางส่วนเป็นข้อผิดพลาดธรรมดา และฉันคิดว่านี่เป็นหนึ่งในกรณีเหล่านี้ ... แม้ว่ามาตรฐานจะบอกว่ามันเป็นลำดับต่อเนื่องแม้แต่นักพัฒนาที่มีประสบการณ์ก็ไม่ได้พูดด้วยความมั่นใจ (โดยไม่ต้องค้นหามาตรฐาน)
Tomer W

22

คุณต้องใส่รหัสนั้นลงในคอนสตรัคเตอร์ของคลาสของคุณ:

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

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

เหตุผลก็คือคุณไม่สามารถใช้ตัวแปรอินสแตนซ์หนึ่งเพื่อเริ่มต้นตัวแปรอื่นโดยใช้ตัวเริ่มต้นฟิลด์


10

คุณสามารถใช้แบบนี้

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

11
ยินดีต้อนรับสู่ Stack Overflow! แม้ว่าข้อมูลโค้ดนี้จะช่วยแก้ปัญหาได้ แต่การมีคำอธิบายจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้มาก โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและบุคคลเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำโค้ดของคุณ โปรดพยายามอย่าเบียดโค้ดของคุณด้วยความคิดเห็นเชิงอธิบายเนื่องจากจะช่วยลดความสามารถในการอ่านโค้ดและคำอธิบาย
jmattheis

3
มันใช้ => แทน = จึงทำให้เป็นคุณสมบัติ
Vincent

3
โปรดใช้เทคนิคนี้อย่างระมัดระวังเนื่องจากการใช้=>ไม่ได้กำหนดค่าจริง แต่จะรันโค้ดทุกครั้งที่defaultReminderเข้าถึง สิ่งนี้อาจไม่ได้ตั้งใจและส่งผลกระทบเชิงลบต่อประสิทธิภาพหรือสร้างแรงกดดันที่ไม่ต้องการให้กับ GC ฯลฯ
Smilediver

4

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];เป็นตัวเริ่มต้นฟิลด์และดำเนินการก่อน (ก่อนฟิลด์ใด ๆ ที่ไม่มีตัวเริ่มต้นจะถูกตั้งค่าเป็นค่าเริ่มต้นและก่อนที่จะเรียกใช้ตัวสร้างอินสแตนซ์ที่เรียกใช้) ฟิลด์อินสแตนซ์ที่ไม่มีตัวเริ่มต้นจะมีเฉพาะค่ากฎหมาย (ค่าเริ่มต้น) หลังจากที่ตัวเริ่มต้นฟิลด์อินสแตนซ์ทั้งหมดเสร็จสมบูรณ์ เนื่องจากลำดับการเริ่มต้นตัวสร้างอินสแตนซ์จะถูกดำเนินการครั้งสุดท้ายซึ่งเป็นสาเหตุที่ทำให้อินสแตนซ์ไม่ถูกสร้างขึ้นในขณะที่เรียกใช้ตัวเริ่มต้น ดังนั้นคอมไพลเลอร์ไม่สามารถอนุญาตให้อ้างถึงคุณสมบัติอินสแตนซ์ (หรือฟิลด์) ใด ๆ ก่อนที่อินสแตนซ์คลาสจะถูกสร้างอย่างสมบูรณ์ เนื่องจากการเข้าถึงตัวแปรอินสแตนซ์เช่นreminderการอ้างถึงอินสแตนซ์โดยปริยาย (this )เพื่อบอกตำแหน่งหน่วยความจำที่เป็นรูปธรรมของอินสแตนซ์ที่จะใช้กับคอมไพเลอร์

นี่คือสาเหตุที่thisไม่อนุญาตให้ใช้ในโปรแกรมเริ่มต้นฟิลด์อินสแตนซ์

ตัวเริ่มต้นตัวแปรสำหรับฟิลด์อินสแตนซ์ไม่สามารถอ้างอิงอินสแตนซ์ที่สร้างขึ้น ดังนั้นจึงเป็นข้อผิดพลาดเวลารวบรวมเพื่อการอ้างอิงนี้ในการเริ่มต้นตัวแปรตามที่มันเป็นข้อผิดพลาดรวบรวมเวลาสำหรับการเริ่มต้นตัวแปรอ้างอิงสมาชิกเช่นใดผ่าน simple_name

สมาชิกประเภทเดียวที่รับประกันว่าจะเริ่มต้นก่อนที่จะเรียกใช้ตัวเริ่มต้นฟิลด์อินสแตนซ์คือตัวเริ่มต้นฟิลด์คลาส (คงที่) และตัวสร้างคลาส (คงที่) และวิธีการคลาส เนื่องจากสมาชิกแบบคงที่เป็นอินสแตนซ์อิสระจึงสามารถอ้างอิงได้ตลอดเวลา:

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

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

สำหรับรายละเอียดเพิ่มเติมผมขอแนะนำให้เอกสารนี้ไมโครซอฟท์เอกสาร: การประกาศคลาส

staticวิธีการนี้ที่สนามอินสแตนซ์ที่อ้างอิงสมาชิกอีกตัวอย่างหนึ่งในการเริ่มต้นค่าของมันจะต้องเริ่มต้นจากตัวสร้างอินสแตนซ์หรือสมาชิกอ้างอิงจะต้องประกาศ

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