คุณสมบัติ. NET - ใช้คุณสมบัติส่วนตัวหรือตั้งค่าแบบอ่านอย่างเดียว?


45

ฉันควรใช้ Private Set ในสถานการณ์ใดเมื่อเทียบกับการทำให้เป็นคุณสมบัติแบบอ่านอย่างเดียว พิจารณาตัวอย่างที่ง่ายสองตัวอย่างด้านล่าง

ตัวอย่างแรก:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

ตัวอย่างที่สอง:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

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


public string Name { get; protected set; }ผ่านการสืบทอด
samis

คำตอบ:


42

private setมีเหตุผลสองสามที่จะใช้

1) ถ้าคุณไม่ได้ใช้เขตข้อมูลสำรองเลยและต้องการคุณสมบัติอัตโนมัติแบบอ่านอย่างเดียว:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) หากคุณต้องการทำงานพิเศษเมื่อคุณแก้ไขตัวแปรภายในชั้นเรียนของคุณและต้องการจับภาพนั้นในที่เดียว:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

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


1
เพียงแค่เพิ่มสิ่งนี้เพราะคำถามนี้ยังมีแท็ก vb.net แต่ใน vb.net คุณต้องระบุผู้สนับสนุนหากคุณใช้แบบส่วนตัวไม่ว่าจะรับหรือตั้งค่า ดังนั้นใน vb.net จริง ๆ แล้วมันทำงานน้อยกว่าที่จะทำให้คุณสมบัติอ่านอย่างเดียวฉันคิดว่า
user643192

ฉันไม่เคยรู้เรื่องprivate setนี้เลย :-)
Afzaal Ahmad Zeeshan

9
การปรับปรุงสำหรับผู้ที่อ่านคำตอบนี้ในปี 2016 C # 6.0 public string Name { get; }ได้เปิดอ่านได้อย่างเดียวอัตโนมัติคุณสมบัติที่ช่วยให้คุณมีคุณสมบัติที่อ่านได้อย่างเดียวโดยไม่ต้องสำรองข้อมูล: หากคุณไม่ต้องการคุณสมบัติที่ไม่แน่นอนนั่นคือไวยากรณ์ที่ต้องการในขณะนี้
Alexey

4
เหตุผลหนึ่งที่ดีมากที่จะไม่ใช้private setคือมันไม่เปลี่ยนรูปเหมือนที่เราชอบแกล้งทำเป็น หากคุณต้องการใช้คลาสที่ไม่เปลี่ยนรูปแบบอย่างแท้จริงการอ่านอย่างเดียวคือสิ่งที่จำเป็น
RubberDuck

อาจเป็นเหตุผลด้านประสิทธิภาพที่ไม่ควรใช้แบบอ่านอย่างเดียว ดูเหมือนว่าจะทำให้การคัดลอก struct ไม่จำเป็นเมื่อเข้าถึงวิธีการของฟิลด์ struct แบบอ่านอย่างเดียว codeblog.jonskeet.uk/2014/07/16/…
Triynko

28

ใช้ชุดส่วนตัวเมื่อคุณต้องการหมาไม่สามารถเข้าถึงจากภายนอก

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

ทดสอบสิ่งนี้:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}

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

NB ไวยากรณ์ VB.NET ที่สอดคล้องกับคำสำคัญ C # readonlyคือการใช้ReadOnlyกับฟิลด์แทนคุณสมบัติ
Zev Spitz

8

ฉันขอแนะนำตัวเลือกที่สามได้ไหม?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

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

อย่างไรก็ตามอย่างที่คุณพูดไม่มีใครตอบถูก


ฉันเชื่อว่า 'ชุดส่วนตัว' มีความหมายพิเศษในคอมไพเลอร์ (ไม่ใช่เพียงแค่แสดงเป็นตัวเข้าถึงส่วนตัว) เป็นกรณีนี้ด้วยชุดป้องกันหรือไม่ ถ้าไม่ความหมายของความหมายเทียบเท่ากับชุดป้องกันหากเซตส่วนตัวมีความหมายพิเศษที่ไหน ฉันไม่สามารถค้นหาเอกสารที่อธิบายเรื่องนี้ได้
สปราก

1
+1 แต่ฉันจะเรียกวิธีการ "เปลี่ยนชื่อ" แทน "SetName"
MattDavey

5

เริ่มต้นใน C # 6.0 คุณสมบัติอัตโนมัติของ getter เท่านั้นถูกเพิ่มเข้าในภาษา ดูที่นี่: https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#getter-only-auto-properties

นี่คือตัวอย่าง:

public class SomeClass
{
    public int GetOnlyInt { get; }

    public int GetOnlyIntWithInitializer { get; } = 25;

    public SomeClass(int getOnlyInt)
    {
        GetOnlyInt = getOnlyInt;
    }
}

4

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

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


2

เมื่อใดก็ตามที่ฉันต้องการเปลี่ยนระดับการเข้าถึงของ setter ฉันมักจะเปลี่ยนเป็น Protected (เฉพาะคลาสนี้และคลาสที่ได้รับเท่านั้นที่สามารถเปลี่ยนค่าได้) หรือ Friend (เฉพาะสมาชิกของแอสเซมบลีของฉันเท่านั้นที่สามารถเปลี่ยนค่าได้)

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


0

และแทบไม่มีการลงโทษประสิทธิภาพเลย ...

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

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

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