我不了解需要以C#2开头的私有设置器。
对我来说,使用setter方法可以让用户在该类中设置一些变量。这样做时,我们不会将变量直接暴露给用户。相反,我们让他们通过此公共设置器方法来执行此操作。
对我来说这是使用“封装”。有一些论点声称私有设置程序将允许您应用封装。
我不是通过公共setter方法使用封装吗?为什么我们需要私人二传手?
不可变的类和带有私有二传手的类有什么区别?
Answers:
逻辑上。
私有设置器的存在是因为您可以使用auto属性:
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 ..那更好!!
Oh crap!! I can't access it from my own class从C#6.0开始,这仅在初始化阶段之外才是正确的。请参阅我的答案stackoverflow.com/a/34223746/198797
{get; }中不等于{ get; private set; }。对于第一种方式,property.GetSetMethod(true)返回null,而第二种true。这让我感到惊讶。
如果您拥有只读属性,并且不想显式声明backing变量,则私有setter很有用。
所以:
public int MyProperty
{
get; private set;
}
是相同的:
private int myProperty;
public int MyProperty
{
get { return myProperty; }
}
对于非自动实现的属性,它为您提供了一种在类中设置属性的一致方法,这样,如果您需要验证等,则只能将其放在一个位置。
为了回答您的最后一个问题,MSDN在专用设置器上有以下说法:
但是,对于仅封装一组值(数据)并且几乎没有行为或几乎没有行为的小型类或结构,建议通过将集合访问器声明为私有来使对象不可变。
引入了C#6.0和自动属性初始化程序的语法器仅在初始化时设置的属性(内联或在构造函数中)不再需要专用设置器。
这些新语法现在可以编译:
内联初始化属性
public class MyClass1 {
public string MyProperty { get; } = "Aloha!"
}
构造函数的初始化属性
public class MyClass2 {
public string MyProperty { get; }
public MyClass2(string myProperty) {
MyProperty = myProperty;
}
}
我不了解需要以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.
}
}
举例来说,您没有通过属性存储实际变量,也没有使用该值来计算某些内容。
在这种情况下,您可以创建一种方法进行计算
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。
为了保持一致性,您可以使用私有设置程序,该属性将Propoerty设为只读,而您可以在代码的内部使用它。
封装意味着对象的状态仅通过定义的接口发生,因此,类可以确保此状态始终有效并且与类的目的保持一致。
因此,在某些情况下,完全公开公开字段是完全符合封装原理的-该字段的所有可能值对所有其他字段的所有其他可能值均有效,因此程序员可以主动决定允许该字段由外部代码自由操纵。
尽管这些情况大多数情况下仅限于大多数都是“普通旧数据”的类。在这方面,它们也不是很有趣,所以关于它们的足够多了。
在其他情况下,在其他语言中,将具有getter和setter方法,类似于int getId()获取值并对其void setId(int val)进行更新。
通过属性,我们可以通过与用于读取和写入字段的方法相同的语法来进行读写。这是一种很好的语法糖,尽管不是必需的。
(实际上,由于反射的工作方式和情况,例如DataBinder.Eval即使某个字段可以正常工作,拥有属性也很方便,但这是另一回事)。
在引入私有设置程序之前(实际上,用C#2进行的更改是在同一块中具有私有设置程序和公共或受保护的getter的语法),我们可以有一个私有方法来完成私有设置程序的工作,因此私人二传手不是必需的。虽然它们很方便,所以尽管只是语法糖,它们还是很有用的。
封装与您的设置者是公开的,私有的,受保护的或内部的设置者无关,而与它们的适当性有关。首先默认每个字段都是私有的(就此而言readonly),然后根据需要添加更改这些字段的成员(属性或方法),并确保对象在更改时保持有效。这确保了类的不变性得到保留,这意味着描述其可以处于的有效状态集的规则永远不会被破坏(构造函数也可以通过确保其以这种有效状态开始而提供帮助)。
至于最后一个问题,要保持不变就意味着一个类没有公共的,受保护的或内部的setter ,也没有任何更改任何字段的公共的,受保护的或内部的方法。这有一定程度,在C#中有3个可能程度:
所有类的实例字段都是readonly,因此即使私有代码也无法更改它。保证它是不可变的(任何试图更改它的东西都不会编译),并且可以在此基础上进行优化。
一个类从外部是不可变的,因为没有公共成员可以进行任何更改,但是不能通过使用保证readonly从内部不进行任何更改。
从外部看,类是不可变的,尽管某些状态作为实现细节已更改。例如,可以记住一个字段,因此从外部尝试获取它只是检索相同的值,而第一个这样的尝试实际上是计算出该值,然后将其存储以供后续尝试使用。
是的,您通过使用属性来使用封装,但是封装的细微之处比仅仅控制属性的读取和写入方式要多。拒绝从类外部设置属性对于鲁棒性和性能都很有用。
不变类是一经创建就不会更改的类,因此需要私有的setter(或根本不需要setter)来保护属性。
在C#3中引入的属性简写形式更频繁地使用私有设置器。在C#2中,通常只省略设置器,设置时直接访问私有数据。
这个性质:
public int Size { get; private set; }
是相同的:
private int _size;
public int Size {
get { return _size; }
private set { _size = value; }
}
除了,后备变量的名称是由编译器内部创建的,因此您不能直接访问它。
使用shorthand属性,需要使用私有setter创建只读属性,因为您不能直接访问backing变量。
get和set之前都不能使用不同的访问修饰符。另外,我认为您正在混合使用2.0和3.0,因为您所指的自动实现的缩写是3.0。
我不了解需要以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; }
}
}
编辑:固定代码标签编辑:示例包含已更正的错误
归功于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);
}
}
当我尝试从课堂之外进行设置时,请参见下面的屏幕截图。
private File settingsFile = null;然后在一个构造函数中:if (settingsFile == null) { settingsFile = GetSettingsFile() };。像这样的重构代码有时让我哭了:)。仅仅因为您可以在构造函数之前设置一个成员,并不意味着您应该(对于多个构造函数)使它很难遵循逻辑。专用设置器会强制您在构造函数或更高版本内部设置值。