ฉันควรเลือกคุณสมบัติที่มีหรือไม่มีฟิลด์ส่วนตัวหรือไม่?


16

codebase ที่ฉันกำลังทำงานอยู่ในขณะนี้มีแบบแผนของการใช้เขตข้อมูลส่วนตัวและคุณสมบัติสาธารณะ ตัวอย่างเช่นคลาสส่วนใหญ่มีสมาชิกกำหนดไว้ดังนี้:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

ฉันรู้ว่าสิ่งเหล่านี้สามารถเขียนใหม่ได้โดยไม่ต้องมีคุณสมบัติส่วนตัวภายใน:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

ฉันต้องการป้อนข้อมูลเกี่ยวกับสิ่งนี้:

  • มีเหตุผลที่ดีหรือไม่ที่จะชอบสไตล์ที่เก่ากว่าและชัดเจนมากกว่าสไตล์ที่ใหม่กว่ากระชับกว่าหรือไม่
  • ฉันควรจะเขียนคลาสใหม่ใด ๆ โดยใช้สไตล์ที่กระชับหรือฉันควรจะพยายามจับคู่รหัสที่เก่ากว่าเพื่อความมั่นคงหรือไม่ ความสอดคล้องมีมูลค่าเพียงพอในกรณีนี้เพื่อจัดรูปแบบที่เก่ากว่าหรือไม่


@PeterK ให้ข้อมูล ไม่ตอบว่าฉันควรกังวลเกี่ยวกับการรักษารูปแบบของโปรแกรมที่เหลือหรือไม่หรือว่าเป็นรายละเอียดเล็ก ๆ น้อย ๆ ที่ไม่สำคัญ
KChaloux

@KChaloux: เข้าใจแล้ว! นั่นเป็นเหตุผลที่มันเป็นความคิดเห็นไม่ใช่คำตอบ :-)
Peter K.

@PeterK Fair 'nuff = p
KChaloux

1
อีกเหตุผลหนึ่งที่ไม่ควรเปลี่ยนคือหากมีสิ่งใดผ่านสายกับ WCF - คุณสมบัติอัตโนมัติมีปัญหาสัญญา (เนื่องจากอักขระที่ไม่ถูกต้องในชื่อเขตข้อมูลสำรองที่สร้างโดย. NET)
Leon

คำตอบ:


16

มีสองสามกรณีที่ยังคงเรียกว่าสไตล์ "เก่า" ที่เรียกว่า:

ตอบ: ประเภทที่ไม่เปลี่ยนรูปโดยใช้ภาษาที่ไม่เปลี่ยนรูปแบบ readonlyปรับปรุงใน C # ค้างค่าที่หลังจากการก่อสร้าง ไม่มีทางที่จะเลียนแบบสิ่งนี้ได้ด้วยคุณสมบัติที่ดำเนินการโดยอัตโนมัติ (ยัง)

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: ผู้ได้รับ / setters ของคุณมีเหตุผลใด ๆ รหัส WPF และ Silverlight (และที่คล้ายกัน) ที่ผูกกับข้อมูลในคลาสของคุณจะดำเนินการINotifyPropertyChangedดังนี้:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

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


2
ดูเหมือนว่าทุกคนจะเห็นด้วยกับรูปแบบใหม่ คุณได้รับ +1 และเครดิตคำตอบสำหรับบอกฉันเกี่ยวกับความจำเป็นของฟิลด์ที่มีreadonlyตัวแก้ไขซึ่งฉันไม่รู้ = D
KChaloux

3
บางคน (รวมถึงฉัน) พิจารณาคุณสมบัติอัตโนมัติพร้อมตัวเข้าถึงส่วนตัว "ดีพอ" สำหรับคุณสมบัติที่ไม่เปลี่ยนรูป จริงพวกเขาอาจไม่เปลี่ยนรูปไม่ได้อย่างแท้จริง แต่คุณต้องระวังไม่ให้ใช้ setter นอกตัวสร้าง
svick

2
อีก reaon คือถ้าคุณต้องการส่งผ่านค่าrefที่ไม่สามารถใช้งานได้กับคุณสมบัติดังนั้นคุณต้องใช้ฟิลด์
stijn

1
เครื่องมืออย่าง Fody สามารถใช้งานได้INotifyPropertyChangedโดยไม่จำเป็นต้องใช้ข้อมูลสำรองที่ชัดเจน github.com/Fody/PropertyChanged
Sebastian Redl

3
รับทราบ: C # 6 อนุญาตสำหรับ "คุณสมบัติอัตโนมัติเท่านั้นทะเยอทะยาน" ซึ่งมีในบรรทัดเดียวสิ่งที่ตัวอย่างของฉัน "A" ทำ
Jesse C. Slicer

6

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

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

ฉันคิดว่าข้อผิดพลาดแบบนี้อาจเกิดขึ้นได้อย่างง่ายดายและค่อนข้างยากที่จะมองเห็น

และถ้าคุณต้องการความมั่นคงให้ทำอย่างอื่น: เปลี่ยนรหัสที่ใช้เขตข้อมูลสำรองเป็นรหัสที่ใช้คุณสมบัติอัตโนมัติ นี่เป็นเรื่องง่ายโดยเฉพาะอย่างยิ่งถ้าคุณใช้เครื่องมือ refactoring เช่น ReSharper (และถ้าคุณไม่ใช้สิ่งนั้นฉันคิดว่าคุณควรเริ่ม)

แน่นอนว่ายังมีกรณีที่จำเป็นต้องมีเขตข้อมูลสำรอง แต่ฉันคิดว่ามันไม่เกี่ยวข้องกับคำถามนี้


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

1

แม้ว่าคำถามจะค่อนข้างเก่า แต่ฉันก็คิดว่าจะเพิ่มคะแนนสองสามคะแนนซึ่งฉันอ่านจากหนังสือที่มีมูลค่าถึงที่นี่

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

  2. เมื่อทำการดีบั๊กคุณไม่สามารถวางเบรกพอยต์บนวิธีรับหรือตั้งค่า AIP ดังนั้นคุณจึงไม่สามารถตรวจพบได้ง่ายเมื่อมีการรับแอปพลิเคชันหรือตั้งค่าคุณสมบัติ คุณสามารถตั้งค่าเบรกพอยต์บนจุดพักที่ใช้งานด้วยตนเอง


3
# 1 - ส่วนใหญ่ serializers ละเว้นคุณสมบัติส่วนตัว / เขตข้อมูลโดยค่าเริ่มต้น; ฉันไม่เคยพบปัญหาที่คุณพูดถึง # 2 - สิ่งนี้ได้รับการแก้ไขใน VS2015 และสามารถแก้ไขได้ใน VS2013 ดูบทความนี้ Visual Studio นิตยสาร
Brian

-3

มีเหตุผลที่ดีหรือไม่ที่จะชอบสไตล์ที่เก่ากว่าและชัดเจนมากกว่าสไตล์ที่ใหม่กว่ากระชับกว่าหรือไม่

ไม่ได้จริงๆ แม้ว่าฉันจะยืนยันว่าเมื่อใดก็ตามที่คุณมีทรัพย์สินผ่านเช่นนั้นคุณควรใช้เขตข้อมูลสาธารณะเพื่อเริ่มต้นด้วย

ฉันควรจะเขียนคลาสใหม่ใด ๆ โดยใช้สไตล์ที่กระชับหรือฉันควรจะพยายามจับคู่รหัสที่เก่ากว่าเพื่อความมั่นคงหรือไม่ ความสอดคล้องมีมูลค่าเพียงพอในกรณีนี้เพื่อจัดรูปแบบที่เก่ากว่าหรือไม่

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



@svick - Bah นอกเสียจากว่าคุณกำลังไตร่ตรองกับประเภทนั้น หากคุณเปิดเผยประเภทสาธารณะในรูปแบบบริการหรือส่วนต่อประสานห้องสมุดคุณจะต้องเปิดเผยส่วนต่อประสานที่ต้องการคุณสมบัติต่อไป OP ไม่ได้ทำสิ่งเหล่านี้
Telastyn

2
ไม่มีเหตุผลทางเทคนิคในกรณีที่ จำกัด เหล่านี้ อย่าลืมคุณสมบัติต่าง ๆ ใน debugger, class diagrams, intellisense และเมื่อคุณคิดว่าคุณไม่ได้ทำการไตร่ตรองกรอบงาน NET คืออะไร WinForms, WPF และส่วนประกอบอื่น ๆ หลายตัวใช้การสะท้อนกลับภายในและพวกมันจัดการคุณสมบัติแยกต่างหากดังนั้นคำแนะนำในการใช้ฟิลด์สาธารณะจะได้รับการพัฒนาอย่างไม่ดีจากผู้พัฒนาซอฟต์แวร์จำนวนมากในเว็บไซต์นี้
Kevin McCormick

1
@KevinMcCormick และเฟรมเวิร์กทำงานได้ดีอย่างสมบูรณ์ (จากสิ่งที่ฉันเข้าใจ) ด้วยฟิลด์ มันไม่สำคัญเลยตั้งแต่ตอนนี้มีคุณสมบัติอัตโนมัติ แต่ปัญหาที่ใหญ่กว่าก็คือการทำให้สิ่งต่างๆเป็นที่สาธารณะในตอนแรก ไกลมากเกินไปคนจำนวนมากคิดว่าการทำสิ่งต่าง ๆ เพียงคุณสมบัติอย่างใดช่วยให้พวกเขาพ้นจาก 'ไม่ทำฟิลด์สาธารณะ'
Telastyn

2
Framework จัดการกับพวกเขาแตกต่างกัน: ลองใช้เขตข้อมูลสาธารณะใน EF POCO ผูกไว้กับ DataGridView ใช้ PropertyGrid ฯลฯ ฉันเพิ่งทำการค้นหาอย่างรวดเร็วใน Decompiler ของฉันไปที่ Type.GetProperties / GetProperty และ Framework ทำการเรียกวิธีนี้ หลายร้อยครั้ง แต่ไม่ใช่กับ GetField บรรทัดล่างมีความแตกต่างและการแนะนำให้ใช้คุณสมบัติสำหรับข้อมูลสาธารณะนั้นส่วนหนึ่งขึ้นอยู่กับข้อเท็จจริงนี้ สิ่งนี้ไม่ได้ จำกัด การใช้งานฟิลด์ แต่กรณีนี้จำเป็นต้องมีเหตุผล อย่างไรก็ตามประเด็นอื่น ๆ ของคุณเป็นความจริงอย่างแน่นอนการเปิดเผยข้อมูลสาธารณะอย่างไม่ระมัดระวังนั้นเป็นความคิดที่ไม่ดีเสมอไป
Kevin McCormick
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.