ความแตกต่างระหว่างคุณสมบัติและฟิลด์ใน C # 3.0+


140

ฉันรู้ว่ามันน่าจะเป็นสิ่งที่ซ้ำกันของความแตกต่างระหว่างสนามและสถานที่ให้บริการใน C # คืออะไร? แต่คำถามของฉันมีความแตกต่างเล็กน้อย (จากมุมมองของฉัน):

เมื่อฉันรู้ว่า

  • ฉันจะไม่ใช้คลาสของฉันกับ "เทคนิคที่ใช้งานได้กับคุณสมบัติ" เท่านั้นและ
  • ฉันจะไม่ใช้รหัสตรวจสอบใน getter / setter

มีความแตกต่าง (ยกเว้นสไตล์ / การพัฒนาในอนาคต) เช่นการควบคุมบางอย่างในการตั้งค่าคุณสมบัติหรือไม่?

มีความแตกต่างเพิ่มเติมระหว่าง:

public string MyString { get; set; }

และ

public string myString;

(ฉันทราบว่าเวอร์ชันแรกต้องใช้ C # 3.0 ขึ้นไปและคอมไพเลอร์สร้างฟิลด์ส่วนตัว)


คำตอบ:


117

encapsulation

ในอินสแตนซ์ที่สองที่คุณเพิ่งกำหนดตัวแปรในครั้งแรกมี getter / setter รอบ ๆ ตัวแปร ดังนั้นหากคุณตัดสินใจว่าต้องการตรวจสอบความถูกต้องของตัวแปรในภายหลัง - มันจะง่ายขึ้นมาก

เมื่อรวมเข้าด้วยกันจะปรากฏใน Intellisense :)

แก้ไข:อัปเดตสำหรับคำถามที่อัปเดตของ OPs - หากคุณต้องการที่จะเพิกเฉยต่อคำแนะนำอื่น ๆ ที่นี่เหตุผลอื่นก็คือมันไม่ได้ออกแบบ OO ได้ดี และถ้าคุณไม่ได้มีเหตุผลที่ดีมากสำหรับการทำมันมักจะเลือกสถานที่ให้บริการมากกว่าตัวแปรสาธารณะ / สนาม


9
ทำไมจะง่ายกว่า อะไรทำให้ฉันไม่สามารถเปลี่ยนเขตข้อมูลเป็นคุณสมบัติและเพิ่มเขตข้อมูลสำรองส่วนตัวได้ สิ่งนี้จะส่งผลกระทบต่อรหัสโทรอย่างไร
Serge Wautier

30
@Serge - มันมีผลต่อรหัสที่คอมไพล์แล้ว ตัวอย่างเช่นหากคุณกำลังพัฒนาไลบรารีที่ใช้งานโดยหลายแอปพลิเคชันการเปลี่ยนเขตข้อมูลเป็นคุณสมบัติในไลบรารีนั้นจะต้องมีการคอมไพล์ซ้ำของแต่ละแอปพลิเคชัน หากเป็นคุณสมบัติคุณสามารถอัปเดตคุณสมบัติได้โดยไม่ต้องกังวล
ดัสตินแคมป์เบล

ฉันเห็นด้วยกับคุณฉันใช้คุณสมบัติเสมอ ฉันแค่อยากรู้เกี่ยวกับความแตกต่างที่เป็นไปได้
p4bl0

24
หากการบริโภครหัสซ้ำอีกครั้งในเวลาเดียวกันกับคลาสที่ได้รับผลกระทบ (ดังนั้นสิ่งที่เป็นส่วนตัวหรือภายในโดยไม่ต้องมองเห็นภายในปลอดภัย 100%) จากนั้นทำให้มันเป็นเขตข้อมูลที่ตกลงอย่างสมบูรณ์
ShuggyCoUk

1
ว้าว Shuggy ความคิดเห็นของคุณเป็นคำตอบที่ฉันกำลังมองหา!
p4bl0

160

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

นี่คือรายการความแตกต่าง:

  • ฟิลด์สามารถใช้เป็นอินพุตสำหรับout/refอาร์กิวเมนต์ คุณสมบัติไม่สามารถ
  • เขตข้อมูลจะให้ผลลัพธ์เดียวกันเสมอเมื่อถูกเรียกหลายครั้ง (หากเราทิ้งปัญหาที่มีหลายเธรด) คุณสมบัติเช่นDateTime.Nowไม่เสมอไปกับตัวเอง
  • คุณสมบัติอาจส่งข้อยกเว้น - ฟิลด์จะไม่ทำเช่นนั้น
  • คุณสมบัติอาจมีผลข้างเคียงหรือใช้เวลาดำเนินการนานมาก ฟิลด์จะไม่มีผลข้างเคียงและจะเร็วที่สุดเท่าที่จะทำได้สำหรับประเภทที่กำหนด
  • คุณสมบัติสนับสนุนความสามารถในการเข้าถึงที่แตกต่างกันสำหรับ getters / setters - ฟิลด์ไม่ (แต่สามารถสร้างได้readonly)
  • เมื่อใช้การสะท้อนคุณสมบัติและเขตข้อมูลจะถือว่าแตกต่างกันMemberTypesดังนั้นจึงอยู่ในตำแหน่งที่แตกต่างกัน ( ตัวอย่างเช่นGetFieldsvs GetProperties)
  • JIT Compiler อาจถือว่าการเข้าถึงคุณสมบัติแตกต่างกันมากเมื่อเทียบกับการเข้าถึงฟิลด์ มันอาจคอมไพล์ลงในรหัสเนทีฟเหมือนกัน แต่มีขอบเขตสำหรับความแตกต่าง

11
อย่างไรก็ตามโปรดทราบว่าจุดต่างๆ เหล่านี้ไม่ควรแตกต่างกันหากใช้แนวปฏิบัติที่ดี นั่นคือคุณสมบัติไม่ควรมีผลข้างเคียงและไม่ควรใช้เวลานานในการดำเนินการ
Noldorin

15
@Noldorin: ผมเห็นด้วย แต่โชคร้ายที่ควรจะเป็นคำสำคัญที่นี่ ด้วยฟิลด์ที่รับประกันพฤติกรรม ฉันไม่ได้บอกว่าคุณควรใช้ฟิลด์ แต่สำคัญที่ต้องระวังความแตกต่างทางความหมาย
Brian Rasmussen

4
ใช่ยุติธรรมพอ โปรแกรมเมอร์เริ่มต้นไม่มีเงื่อนงำเกี่ยวกับสิ่งเหล่านี้บ่อยครั้งน่าเสียดาย ...
Noldorin

2
นอกจากนี้เขตข้อมูลอาจมีตัวเริ่มต้นของเขตข้อมูลในขณะที่คุณสมบัติจะต้องเริ่มต้นได้ในตัวสร้าง
Dio F

3
ฉันรู้สึกว่าคำตอบนี้มีขนาดดีกว่าคำตอบที่ยอมรับ ฉันเริ่มคิดว่าวิธีที่ "ยอมรับได้" ในการเลือกคุณสมบัติเหนือทุ่งคือการคิดที่ไม่ดี หากคุณต้องการจัดการกับข้อมูลให้ใช้ฟิลด์ หากคุณต้องการใช้ฟังก์ชันการทำงานกับข้อมูลให้ใช้วิธีการ เนื่องจากคุณสมบัติอาจมีผลข้างเคียงที่คุณไม่ได้รับรู้ (โดยเฉพาะอย่างยิ่งถ้าคุณไม่ได้ออกแบบห้องสมุดและมีเอกสารเล็ก ๆ ) พวกเขาดูเหมือนจะง่ายสำหรับฉันในกรณีส่วนใหญ่
David Peterson

41

ความแตกต่างที่รวดเร็วและชัดเจน

  1. คุณสมบัติสามารถมีคำหลักของผู้เข้าถึง

    public string MyString { get; private set; }
  2. คุณสมบัติสามารถถูกเขียนทับในผู้สืบทอด

    public virtual string MyString { get; protected set; }

1
mmh .. nr 2 น่าสนใจ .. ฉันไม่ได้คิดถึงมันเลย
p4bl0

14

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


11

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

คุณสมบัติมีส่วนร่วมในคลาสอินเตอร์เฟส ตัวอย่างเช่น:

interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
}

อินเทอร์เฟซนี้สามารถทำได้หลายวิธี ตัวอย่างเช่น:

class Person: IPerson
{
    private string _name;
    public string FirstName
    {
        get
        {
            return _name ?? string.Empty;
        }
        set
        {
            if (value == null)
                throw new System.ArgumentNullException("value");
            _name = value;
        }
    }
    ...
}

ในการใช้งานนี้เรากำลังปกป้องทั้งPersonชั้นเรียนจากการเข้าสู่สถานะที่ไม่ถูกต้องเช่นเดียวกับผู้โทรจากการเป็นโมฆะจากคุณสมบัติที่ไม่ได้กำหนด

แต่เราสามารถผลักดันการออกแบบให้ดียิ่งขึ้นไปอีก ตัวอย่างเช่นอินเตอร์เฟสอาจไม่จัดการกับ setter มันค่อนข้างถูกต้องตามกฎหมายที่จะกล่าวว่าผู้ใช้IPersonอินเทอร์เฟซสนใจที่จะรับทรัพย์สินเท่านั้นไม่ใช่ในการตั้งค่า:

interface IPerson
{
    string FirstName { get; }
    string LastName { get; }
}

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

class PersonBuilder: IPersonBuilder
{
    IPerson BuildPerson(IContext context)
    {

        Person person = new Person();

        person.FirstName = context.GetFirstName();
        person.LastName = context.GetLastName();

        return person;

    }
}

...

void Consumer(IPersonBuilder builder, IContext context)
{
    IPerson person = builder.BuildPerson(context);
    Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}

ในรหัสนี้ผู้บริโภคไม่ทราบเกี่ยวกับผู้ตั้งทรัพย์สิน - ไม่ใช่ธุรกิจของเขาที่จะรู้เกี่ยวกับมัน ผู้บริโภคต้องการเพียงแค่ getters และเขาได้รับ getters จากอินเทอร์เฟซคือจากสัญญา

การดำเนินการที่ถูกต้องสมบูรณ์อีกอย่างหนึ่งคือIPersonคลาสบุคคลที่ไม่เปลี่ยนรูปและโรงงานบุคคลที่เกี่ยวข้อง:

class Person: IPerson
{
    public Person(string firstName, string lastName)
    {

        if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))
            throw new System.ArgumentException();

        this.FirstName = firstName;
        this.LastName = lastName;

    }

    public string FirstName { get; private set; }

    public string LastName { get; private set; }

}

...

class PersonFactory: IPersonFactory
{
    public IPerson CreatePerson(string firstName, string lastName)
    {
        return new Person(firstName, lastName);
    }
}
...
void Consumer(IPersonFactory factory)
{
    IPerson person = factory.CreatePerson("John", "Doe");
    Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}

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


7

คนแรก:

public string MyString {get; set; }

เป็นทรัพย์สิน อันที่สอง (public string MyString ) หมายถึงฟิลด์

ความแตกต่างคือเทคนิคบางอย่าง (ASP.NET databinding สำหรับอินสแตนซ์) ใช้งานได้กับคุณสมบัติเท่านั้นไม่ใช่ในฟิลด์ สิ่งเดียวกันนี้เป็นจริงสำหรับ XML Serialization: มีเพียงคุณสมบัติเท่านั้นที่ถูกทำให้เป็นอนุกรม


8
ไม่ถูกต้อง. การทำให้เป็นอันดับ XML ไม่เป็นอันดับเขตข้อมูลสาธารณะ
Serge Wautier

2
อาจจะ. แต่เมื่อคุณสร้างแหล่งข้อมูลออบเจ็กต์จากคลาสคุณจะได้รับการใช้คุณสมบัติไม่ใช่ฟิลด์ (ถ้าข้าไม่ได้ทำอะไรผิด: P)
Svish

เป็นการดีที่จะ DRY;) แต่ฉันเขียนอีกครั้งฉันชอบบทบาทที่แข็งแกร่งของทรัพย์สินในภาษา C # มีการใช้งานที่ดีขึ้นกว่าใน Java (จากจุดเริ่มต้น) โซลูชั่น. net จำนวนมากอาจใช้งานได้กับคุณสมบัติเท่านั้น WPF, ASPX และอีกมากมาย
Jacek Cz

3

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

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

คิดว่าคุณสมบัติเป็นน้ำตาลเชิงซ้อนสำหรับฟังก์ชั่น getXXX () / setXXX () นี่คือวิธีการใช้งานเบื้องหลัง


1

มีความแตกต่างที่สำคัญอย่างหนึ่งระหว่างฟิลด์และคุณสมบัติ

เมื่อใช้ WPF คุณสามารถผูกเข้ากับคุณสมบัติสาธารณะเท่านั้น การเชื่อมต่อกับเขตข้อมูลสาธารณะจะไม่ทำงาน สิ่งนี้เป็นจริงแม้ในขณะที่ไม่ได้ใช้งานINotifyPropertyChanged(แม้ว่าคุณควรทำก็ตาม)


เป็นการดีที่จะ DRY;) แต่ฉันเขียนอีกครั้งฉันชอบบทบาทที่แข็งแกร่งของทรัพย์สินในภาษา C # มีการใช้งานที่ดีขึ้นกว่าใน Java (จากจุดเริ่มต้น) โซลูชั่น. net จำนวนมากอาจใช้งานได้กับคุณสมบัติเท่านั้น WPF, ASPX และอีกมากมาย
Jacek Cz

1

ในบรรดาคำตอบและตัวอย่างอื่น ๆ ฉันคิดว่าตัวอย่างนี้มีประโยชน์ในบางสถานการณ์

ตัวอย่างเช่นสมมติว่าคุณมีสิ่งต่อไปนี้:OnChange property

public Action OnChange { get; set; }

หากคุณต้องการใช้ผู้ร่วมประชุมมากกว่าที่คุณต้องการเปลี่ยนOnChangeให้เป็นfieldแบบนี้:

public event Action OnChange = delegate {};

ในสถานการณ์เช่นนี้เราปกป้องเขตข้อมูลของเราจากการเข้าถึงหรือการแก้ไขที่ไม่พึงประสงค์


0

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


"เสมอ" เป็นคำที่พูดยาก ในคุณสมบัติ C # (ดีกว่าใน Java) มีตำแหน่งที่แข็งแกร่งคือ (อาจไม่มีข้อยกเว้น) หลัก / วิธีการ "ผูกมัด" ใน ASP, WPF และอื่น ๆ แต่กระนั้นฉันสามารถจินตนาการการออกแบบที่มีข้อมูลไม่เป็นสถานที่ให้บริการมีความรู้สึก (บางครั้ง)
Jacek Cz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.