C#属性和它们的不同用法

我学习C#的部分来自于不同的资源,因为每个人都有不同的学习起点。一本书重点依赖于可以使生活更简单的Visual Studio,会使人忽视建立.NET项目的本质,一旦可以从scratch中得到一切,那确实是挠痒痒,进步缓慢。

当我学会如何创建类的时候,我学到的一种结构就是属性。属性有一点令人困惑的语法,如下面描述的那样:

1 public class MyRiches
2 {
3     // Normal Data Members
4     public int Money;
5     // Properties
6     public int GoldInKgs { get; set; }
7 }

当我学习属性的时候,我一直在思考为什么我要花额外的精力去定义一种数据成员,当我可以用一种更简单和更为熟悉的方式来创建时。我认为它是语法糖,也仅仅是用于可见的糖尿病。然后在我的代码库中,我看到了属性的一种有趣的用法。

 1 public class MyClass
 2 {
 3     public IList<SomeType> Property
 4     {
 5         get
 6         {
 7             if(m_property == null)
 8             {
 9                 m_property = new List<SomeType>();
10             }
11             return m_property;
12         }
13         set
14         {
15             m_property = value;
16         }
17     }
18     private IList<SomeType> m_property;
19 }

我突然担心我可疑的C#知识。因此,我开启了一段旅程,去探索真实的用处和各种模式的用法(不用担心,我稍后会解释这个示例)。

属性到底是什么呢?

在讨论属性为什么存在之前,我们先假设属性不存在。现在类中有一个私有的数据成员,但是我需要在其它类中访问它的值,却不允许修改它的值,示例如下:

 1 public class Bank
 2 {
 3     private int AccountBalance;
 4 }
 5 
 6 public class Me
 7 {
 8     var myBankAccount = new Bank();
 9     var myMoney = myBankAccount.AccountBalance;
10 }

现在我不能直接访问AccountBalance属性。我将它设置为public,尽管对于我来说比较有趣,但是可能银行不这么认为,因为我会这么做:

1 // The easy way of becoming a millionaire
2 myBankAccount.AccountBalance = 1000000;

所以说解决方案是什么呢?对的,getters(和setters,同胞兄弟)。我们可以很容易地定义一个getter数据成员,可以提供数值但是不会让我们修改它,像下面这样:

 1 public class Bank
 2 {
 3     private int AccountBalance;
 4     public int getAccountBalance()
 5     {
 6         return AccountBalance;
 7     }
 8 }
 9 
10 public class Me
11 {
12     var myBankAccount = new Bank();
13     var myMoney = myBankAccount.getAccountBalance();
14 }

如果你习惯用Eclipse写Java,你知道它有一个功能可以自动创建getters和setters,我相信C#的创建者在Visual Studio中已经加载了此功能,但是对于没有用过的人来说,写getters和setters是个冗长的函数。

这就是为什么,C#开发人员创建了属性:

属性提供了一种可以访问私有数据成员的机制

如果你不是一个喜欢繁琐的人,那基本上意味着,写自定义的getter和setter函数是一种简便的快捷方式。或者,用另一种方式来说,它们其实是一种语法糖,不想被C#属性的语法规则来打扰的话,可以参考这个链接:https://www.geeksforgeeks.org/c-sharp-properties/

现在有趣的部分来了,属性的各种使用模式。get和set不仅仅是作作样子,属性可以自定义并编译运行各种有趣且有用的代码模式。

C#属性的延迟加载

属性可以利用延迟加载功能运行缓存:

 

 1 private int m_IncomeTax;
 2 
 3 public int IncomeTax
 4 {
 5     get
 6     {
 7         if(m_IncomeTax == null)
 8         {
 9             m_IncomeTax = AReallyLongComputationForTax();
10         }
11         return m_IncomeTax;
12     }
13 }

 

这是属性的基本用法,当然不是限制性访问。

Future Proofing Code

你想要维护类的API,但是逻辑或运算发生改变。在不影响你创建的类的API的情况下,你可以更改setter代码。

依然以上面的范例来说,以计算税费为例,要加入cess税,可以像下面示例一样更改getter代码,这样的话IncomeTax属性就会给出总的税费。

 1 // Old
 2 public int IncomeTax
 3 {
 4     get
 5     {
 6         return m_IncomeTax;
 7     }
 8 }
 9 
10 // New
11 public int IncomeTax
12 {
13     get
14     {
15         return m_IncomeTax + Cess;
16     }
17 }

创建一个协议

属性可以为类创建一个协议或API。访问类成员的正确方式可能是私有的或是私有成员的运算。如果你需要对私有成员进行额外计算,这些方式是很有用的。

 一般来说,指针是用来将运行(字段)和API(属性)分离开的。稍后会看到,在不破坏源代码或二进制兼容性的情况下,将逻辑、日志等放在属性中,但是更重要地,你的数据类型想要做什么,而不是它如何去做。

用属性返回Null

开头承诺的解释代码,对于引用类型,如下:

 

 1 public class MyClass
 2 {
 3     public IList<SomeType> Property
 4     {
 5         get
 6         {
 7             if(m_property == null)
 8             {
 9                 m_property = new List<SomeType>();
10             }
11             return m_property;
12         }
13         set
14         {
15             m_property = value;
16         }
17     }
18 
19     private IList<SomeType> m_property;
20 }

这段代码实质上是检验给定的成员是否为NULL,如果为null,首先会进行赋值,然后返回数值。这种方式有很大的好处。假设属性值没有null检查,此后你会确保不会设置一个null值并减少代码数量,检查已写的内容,我们都希望写更少的代码。

最后,我知道属性像是美化了的setters/getters,上面提到的所有好处用setters/getters也能做到,也就是说,他们就仅仅是种语法糖。学会使用他们进行访问控制,而不是使用公共的数据成员,可以使代码更加健康。

 

posted @ 2022-04-01 00:07  chenlight  阅读(71)  评论(0)    收藏  举报