了解私人制定者

时间:2010-10-02 22:57:27

标签: c#

我不明白是否需要使用以C#2开头的私人制定者。

为我设置一个setter方法是让用户在该类中设置一些变量。在这样做时,我们不会将变量直接暴露给用户。相反,我们让他们通过这种公共setter方法来做到这一点。

这对我来说是使用“封装”。有一些争论声称私人制定者会允许你应用封装。

我是不是通过使用公共setter方法使用封装?为什么我们需要私人制定者?

不可变类和具有私有setter的类之间有什么区别?

12 个答案:

答案 0 :(得分:218)

逻辑上。

私人设定者的存在是因为你可以使用自动属性:

public int MyProperty { get; set; }

如果你想让它成为只读,你会怎么做?

public int MyProperty { get; }

哦废话!!我不能从我自己的班级访问它;我应该像普通的属性一样创建它:

private int myProperty;
public int MyProperty { get { return myProperty; } }

嗯......但我失去了“自动财产”功能......

public int MyProperty { get; private set; }

AHHH ..那更好!!

答案 1 :(得分:36)

如果您具有只读属性并且不希望显式声明支持变量,则私有setter非常有用。

所以:

public int MyProperty
{
    get; private set;
}

与:

相同
private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

对于非自动实现的属性,它为您提供了一种从类中设置属性的一致方法,这样,如果您需要验证等,您只能将它放在一个位置。

要回答您的最后一个问题,MSDN会对私人制定者说:

  

但是,对于只封装一组值(数据)并且几乎没有行为的小类或结构,建议通过将set访问器声明为私有来使对象不可变。

来自the MSDN page on Auto Implemented properties

答案 2 :(得分:17)

这很简单。私有setter允许您创建只读公共或受保护的属性。

就是这样。这是唯一的原因。

是的,您可以通过仅指定getter来创建只读属性,但是使用auto-implmeneted属性需要指定get和set,因此如果您希望自动实现的属性是只读的,你必须使用私有的setter。没有其他办法可以做到。

私有设置器确实不是专门为自动实现的只读属性创建的,但由于其他原因,它们的使用有点深奥,主要围绕只读属性以及反射和序列化的使用。

答案 3 :(得分:11)

随着C# 6.0的引入和自动属性初始化程序的语法,仅在初始化时(内联或构造函数内)设置的属性不再需要私有setter。

现在编译这些新语法:

内联初始化属性

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

构造函数初始化属性

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

答案 4 :(得分:5)

  

我不明白是否需要使用以C#2开头的私有设置器。

例如,发票类允许用户在Items属性中添加或删除项目,但不允许用户更改Items引用(即,用户无法将Items属性分配给另一个项目列表对象实例)。


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

答案 5 :(得分:3)

例如,假设您没有通过属性存储实际变量或使用该值来计算某些内容。

在这种情况下,您可以创建一个方法来进行计算

private void Calculate(int value)
{
 //...
}

或者你可以使用

这样做
public int MyProperty {get; private set;}

在这些情况下,我建议使用后者,因为属性重构每个成员元素完整。

除此之外,甚至可以说你用变量映射属性。在这种情况下,您希望在代码中编写如下代码:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

上面的代码看起来很糟糕,因为程序员需要始终谨慎使用MyProperty for Get和myprop for Set。

为了保持一致性,你可以使用私有的setter,它可以将propoerty只读在外面,同时你可以在代码中使用它的setter。

答案 6 :(得分:3)

我认为有几个人围绕着这个跳舞,但对我来说,私人制定者的价值在于你可以封装一个属性的行为,即使在一个类中也是如此。正如abhishek所指出的,如果你想在每次属性更改时触发属性更改事件,但是你不希望对公众进行读/写属性,那么你必须使用私有的setter,或者你必须提高任何修改后备字段的事件。后者容易出错,因为您可能会忘记。相关地,如果更新属性值导致正在执行某些计算或者正在修改其他字段,或者某些内容的延迟初始化,那么您还需要将其包装在私有setter中,而不是必须记住在您制作的任何位置执行它使用支持领域。

答案 7 :(得分:2)

封装意味着对象的状态只通过定义的接口发生,因此,类可以确保此状态始终有效并符合类的目的。

因此,在某些情况下,完全符合封装原则只是公开暴露一个字段 - 该字段的所有可能值对所有其他字段的所有其他可能值都有效,因此程序员可以主动决定允许外部代码自由操作字段。

这些案例主要限于主要是“普通旧数据”的类。在这方面他们也不是很有趣,对他们来说足够了。

在其他情况下,在其他语言中,可以使用getter和setter方法,例如int getId()获取值,void setId(int val)更新它。

属性让我们使用相同的语法来读取和写入我们用于读取和写入字段的方法。这是一个很好的语法糖,虽然不重要。

(实际上,由于反射的工作方式以及诸如DataBinder.Eval之类的情况,即使字段工作正常也可以很方便地获得属性,但这是另一回事。)

直到引入私有的setter(实际上,使用C#2改变的是在同一个块中拥有私有setter和public或protected getter的语法),我们可以使用私有方法来执行私有的工作因此,私人制定者并不是必需的。它们很方便,所以虽然只是语法糖,但它们非常有用。

封装不是关于你的setter(或getter)是公共的,私有的,受保护的还是内部的,而是它们是否适合的问题。从每个字段的默认值开始是私有的(并且就此而言readonly),然后根据需要添加更改这些字段的成员(无论是属性还是方法),并确保对象在更改时保持有效即可。这确保了保持类不变,这意味着描述它可以处于的有效状态集的规则永远不会被破坏(构造函数也有助于确保它以这种有效状态启动)。

至于你的上一个问题,不可变意味着一个类没有公共,受保护或内部的setter 没有更改任何字段的公共,受保护或内部方法。有这样的程度,在C#中有三种可能的程度:

  1. 所有类的实例字段都是readonly,因此即使是私有代码也无法改变它。它保证是不可变的(任何试图改变它的东西都不会编译)并且可能在此背后进行优化。

  2. 一个类从外部是不可变的,因为没有公共成员改变任何东西,但不能保证使用readonly不能从内部改变。

  3. 从外部看,类是不可变的,尽管某些状态是作为实现细节而变化的。例如。一个字段可以被记忆,因此当从外部尝试获取它只是检索相同的值时,第一个这样的尝试实际上计算它然后存储它以便在随后的尝试中检索。

答案 8 :(得分:1)

是的,您正在使用属性进行封装,但封装的细微差别不仅仅是控制属性的读写方式。拒绝从类外部设置的属性对于稳健性和性能都是有用的。

不可变类是一个在创建后不会更改的类,因此需要私有setter(或根本不需要setter)来保护属性。

使用C#3中引入的属性简写来更频繁地使用私有的setter。在C#2中,setter通常只是被省略,私有数据在设置时直接访问。

此属性:

public int Size { get; private set; }

与:

相同
private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

除外,后备变量的名称由编译器在内部创建,因此您无法直接访问它。

使用简写属性需要私有setter来创建只读属性,因为您无法直接访问支持变量。

答案 9 :(得分:1)

  

我不明白是否需要使用以C#2开头的私有设置器。

用例示例:

我有一个应用程序对象'UserInfo'的实例,其中包含一个我不希望向我的类的消费者公开的属性SessionTokenIDV1

我还需要能够从我的班级设置该值。

我的解决方案是如图所示封装属性并使setter变为私有,这样我就可以设置会话令牌的值而不允许实例化代码来设置它(或者甚至在我的情况下看到它)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

编辑:固定代码标记 编辑:示例包含已更正的错误

答案 10 :(得分:0)

如果你想支持以下场景,你需要一个私有的setter(不仅仅是为了这个,但是这应该指出一个很好的理由): 你有一个只在你的类中只读的属性,即只允许类本身改变它,但它可能在构造实例后改变它。 对于绑定,您需要触发一个PropertyChanged事件,最好这应该在(private)属性setter中完成。 实际上,您可以从类中的其他位置触发PropertyChanged事件,但使用私有设置器是“良好的公民身份”,因为您不会在您的课程中分发您的属性更改触发器,而是将其保留在财产,它属于哪里。

答案 11 :(得分:-1)

贷记https://www.dotnetperls.com/property

专用设置器与只读字段相同。它们只能在构造函数中设置。如果尝试从外部进行设置,则会出现编译时错误。

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

当我尝试从课堂之外进行设置时,请参见下面的屏幕截图。

enter image description here