คุณควรสร้างที่พักส่วนตัวหรือไม่?


19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

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

คำตอบ:


17

คำตอบสั้น ๆ : ใช่เมื่อมีความจำเป็น มิฉะนั้นให้ใช้ตัวรับทรัพย์สินที่นำมาใช้โดยอัตโนมัติและผู้ตั้งค่าเช่นprivate string Whatever { get; set;}

  • มันมีประโยชน์มากเมื่อคุณใช้วิธีการปิดโดเมน
  • นอกจากนี้ยังมีประโยชน์เมื่อควรตรวจสอบลอจิกเฉพาะเมื่อคุณตั้งค่า

นี่คือคำอธิบายที่เต็มไปด้วยเมื่อคุณจะใช้ setters ส่วนตัว: การใช้งานคุณสมบัติ C #


นอกจากนี้ยังเป็นเรื่องง่ายที่จะเผยแพร่ในภายหลังถ้าจำเป็น
Tom Pickles

4
คืออะไรวิธีโดเมนใกล้ ?
Trisped

1
ฉันหมายถึง setter ส่วนตัวในทรัพย์สินของชั้นเรียน ควรป้องกันการเข้าถึงโดยตรงเพื่อตั้งค่าจากวัตถุและอำนวยความสะดวกในการตรวจสอบตรรกะบางอย่างก่อนที่จะตั้งค่า
Yusubov

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

12

บางคำตอบที่ดีอยู่ที่นี่แล้ว แต่ฉันคิดว่าพวกเขาส่วนใหญ่คิดถึงจุดหนึ่งของคำถามของคุณ:

ฉันเคยเห็นบางคนที่สร้างคุณสมบัติให้กับสมาชิกทุกคนเป็นส่วนตัวหรือไม่ ... สิ่งนี้มีเหตุผลหรือไม่?

ฉันคิดว่ามันไม่ค่อยจำเป็นสำหรับสมาชิกทุกคนก่อน เริ่มกับ

 private string Whatever;

เมื่อคุณต่อมามาถึงจุดที่คุณต้องห่อหุ้มหรือจุดพักตามเงื่อนไขของสมาชิกที่เฉพาะเจาะจงนี้คุณยังสามารถแทนที่โดยสถานที่ที่มีชื่อเดียวกัน - Whateverในกรณีส่วนใหญ่ไม่ได้เปลี่ยนรหัสที่ใช้ แต่ระวังมีความแตกต่างเล็กน้อยดูคำตอบของ Brian Rasmussen ในโพสต์ SO นี้ (ลิงก์ที่ Emmad Kareem, +1 ได้ให้ไว้ด้วย)


+1 ด้วยคุณถูกต้องคำตอบส่วนใหญ่ไม่ได้รับคะแนนคำถามดั้งเดิมเราเป็นคนไอทีทั่วไป :)
NoChance

การคืนค่าฟิลด์ให้แก่คุณสมบัติหลังจากความจริงแล้วเป็นฝันร้าย ต้องการให้คุณคอมไพล์ผู้บริโภคอีกครั้ง การเปลี่ยนคุณสมบัติอัตโนมัติเพื่อให้มีตรรกะที่กำหนดเองเป็นเรื่องง่าย หากมีโอกาสเล็กน้อยที่รหัสจะเห็น refactor มันจะทำงานได้น้อยลงถ้าคุณใช้คุณสมบัติจากการโจมตี
Dan

@ ด่าน: ในโครงการส่วนใหญ่ที่ฉันได้เห็นความพยายามค่อนข้างเหมือนกันคุณต้องคอมไพล์อีกครั้งในทั้งสองกรณี อย่างไรก็ตามฉันคิดว่าคุณถูกต้องว่าการใช้คุณสมบัติโดยเฉพาะอย่างยิ่งคุณสมบัติอัตโนมัติสามารถช่วยหลีกเลี่ยงปัญหาบางอย่างเกี่ยวกับการสะท้อนหรือการใช้ตัวแปรสมาชิกในพารามิเตอร์ "ออก"
Doc Brown

@DocBrown ไม่ว่าจะในกรณีใด ๆ มีเหตุผลมากมายที่จะทำให้มันง่ายและใช้ฟิลด์ หากคุณสามารถคาดเดา refactor แม้ว่าปัญหาที่ลึกซึ้งก็เป็นสิ่งที่ควรพิจารณา
Dan

7

หนึ่งในข้อได้เปรียบที่ใหญ่ที่สุดของวิธีนี้คือการที่คุณได้รับการควบคุมในเมื่อตัวแปรของคุณเปลี่ยน

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

ด้วยคุณสมบัติคุณสามารถตั้งค่าเบรกพอยต์ในตัวตั้งค่า เมื่อรวมกับเงื่อนไขอาจถูกตอกลงในการวิ่งครั้งเดียว

VC ++ มีจุดพักข้อมูลแต่จะใช้ได้กับรหัสที่ไม่มีการจัดการเท่านั้น


1

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


1

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


ตัวอย่างที่ให้มานั้นเป็นตัวอย่างที่ไม่ดี หม้อไอน้ำและตัวตั้งค่าคุณสมบัติหม้อไอน้ำแบบนี้มักเป็นความคิดที่ไม่ดี หากคุณใช้ C # 3.0 หรือใหม่กว่าคุณสมบัติอัตโนมัติเป็นแนวคิดที่ดีกว่ามาก:

private string Whatever { get; set; };

นี่คือสั้นกว่าสะอาดกว่าและอ่านได้มากขึ้น ในความเป็นจริงมันแทบจะนานกว่าการประกาศตัวแปรสำรอง

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


เพราะฉันคิดเสมอว่ามันเป็นความอัปยศที่คุณไม่มีคุณสมบัติอ่านอย่างเดียวบังคับใช้ความสามารถในการเขียนค่าในตัวสร้าง (ฉันชอบประเภทที่ไม่เปลี่ยนรูป ) แต่สิ่งนี้ถูกเพิ่มใน C # 6.0เป็น "คุณสมบัติเริ่มต้นของคุณสมบัติอัตโนมัติ" ซึ่งช่วยให้คุณทำ:

private string Whatever { get; } = ...;

หรือ

private string Whatever { get; };

พร้อมด้วย

    Whatever = ...;

ในตัวสร้าง


0

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

ตัวอย่างเช่นการปรับเปลี่ยนวิธีการตั้งค่าพื้นฐาน:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}

0

ใช่

ผมเองใช้นี้เป็นกลไกแคชและเราอาจเรียกว่าแคชคุณสมบัติระดับ

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

ตอนนี้ในสถานที่อื่น ๆ ในชั้นเรียนของฉันฉันใช้Usersคุณสมบัติแทนusersสนาม

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

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}

0

สิ่งอื่นที่ควรพิจารณา:

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

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

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

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


0

ฉันรู้ว่านี่เป็นคำถามเก่าและทุกอย่างที่@Yububov กล่าวว่าถูกต้อง แต่ด้วยความเคารพต่อสัญลักษณ์แสดงหัวข้อย่อยที่สองของเขาเกี่ยวกับตรรกะเฉพาะใน setter และเนื่องจากฉันไม่เห็นใครพูดถึงเรื่องนี้เป็นพิเศษตัวอย่างที่สมบูรณ์แบบคือเมื่อชั้นเรียนของคุณ ใช้INotifyPropertyChangedอินเทอร์เฟซ

ใช้ตันในWCFและSilverlightซึ่งจะช่วยให้คุณทราบเมื่อมีการเปลี่ยนแปลงคุณสมบัติเฉพาะผ่านทางตรรกะในตัวตั้งค่าเหมือนในคลาสด้านล่าง:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.