C#箴言之用属性来访问类的私有成员

访问|来访

  在程序中,难免要访问某个对象的私有成员。那么以前实现这类功能的方法有两种,第一种方法最简单,就是把成员访问符从“private”改为“public”即可;而另一个就是提供公有的成员访问函数来进行访问。那么现在用C#编写程序,就不再需要采用前面所说的两种方法了,而直接使用属性来完成。

  首先来看看三种方法的如何实现以及调用的,这里用一个例子来说明,即访问“EmployeeInfo”类的私有成员strName,具体如下表格所示。

  private string strName; 访问方法
修改成员访问符 修改:
private string strName;
为:
public string strName;
EmployeeInfo empNew...;
string strNameValue = empNew.strName;
empNew.strName = "me";
公有成员函数 增加如下两个成员函数:
public string getName()
{
 return strName;
}

public void setName( string Name )
{
 strName = Name;
}

EmployeeInfo empNew...;

string strNameValue = empNew.getName();

empNew.setName( "me" );

属性 增加如下属性:
public string Name
{
 get{ return strName;}
 set{ strName = value; }
}
EmployeeInfo empNew...;
string strNameValue = empNew.Name;
empNew.Name = "me";

  那么这三种方法有什么区别呢,用如下的表格,可以更好的说明三者的区别。

  类的封装性 代码安全性 代码繁琐性 代码效率
修改成员访问符 破坏类的封装 存在潜在危险 简便 最高
公有成员函数 没有破坏 安全 繁琐,而且调用不直接 最低
属性 没有破坏 安全 简便 仅次于第一种方法

    (备注:这里用红色表明每一子项中最不好的)

  因此可以看出使用属性不但没有破坏类的封装性,没有减弱代码的安全性,而且和第一种方法一样简便,只是在效率方面要略低于第一种方法。但总体看来,在C#中用属性来访问类的私有成员是不二的选择。

  不过对于使用属性,以及如上表格所说的,难免会有人产生如下一些疑问。

  疑问一:就是用属性是否能达到成员函数那样的效果,即完成复杂的代码操作。

  其实属性的底层实现是借助于成员函数,只不过这部分转换是由系统帮忙做的,所以在编写属性的时候,可以像编写成员函数一样,即在成员函数中所能写的代码片断,完全可以在属性中套用。下面就贴出属性所转换的微软中间语言(MSIL)代码。

.property instance string Name()
{
 .get instance string NameSpace.EmployeeInfo::get_Name()
 .set instance void NameSpace.EmployeeInfo::set_Name(string)
}// end of property EmployeeInfo::Name

.method public hidebysig specialname instance string get_Name() cil managed
{
 ...
}// end of method EmployeeInfo::get_Name

.method public hidebysig specialname instance void set_Name(string 'value') cil managed
{
 ...
}// end of method EmployeeInfo::set_Name
  如上就是前面EmployeeInfo类的Name属性所转换的中间语言代码(不过省略了函数的具体实现代码,因为这里并不是为了研究中间语言代码,如果需要对这部分有更多地了解,参看中间语言相关书籍)。

  疑问二:就是用属性的效率是否仅次于第一种方法。

  从上面很容易看出,属性在编译的时候会转换成和成员函数一样的代码,那么它的效率应该和成员函数是一样的。其实并不是这样,因为JIT编译器会把属性所转换成的两个成员函数作为内联函数,这样效率会提高很多。(注:内联函数是代码被插入到调用者代码处的函数,通过避免函数调用所产生的额外开销,从而提高执行效率。不过书中也提到,即使不是内联函数,成员函数相对于方法一的效率损失也是微乎其微的。)

  用C#写程序,一提到属性,大家都会编写。其实在属性中,可以产生很多应用,接着来就分别说明。

  <!--[if !supportLists]-->1. <!--[endif]-->在属性中使用索引符,例如像“ArrayList[i]”来访问ArrayList某个成员。这里需要注意的是,属性名以及索引参数的编码格式是固定的,如“this […]”。不过索引参数可以是多个,而且不光支持整型参数,还可以使用其他类型参数。例如:

public ReturnValueType this[ ParType1 parValue1, ParType2 parValue2]
{
 get{...}
 set{...}
}
  <!--[if !supportLists]-->2. <!--[endif]-->可以给属性操作加上互斥锁,以防止多线程操作时而产生的并发错误,具体如下。

public string Name
{
 get
 {
  lock(this)
  {
   return strName;
  }
 }
 set
 {
  lock(this)
  {
   strName = value;
  }
 }
}
  <!--[if !supportLists]-->3. <!--[endif]-->书上还提到属性的其他应用,例如:通过接口来实现在一个类中同时提供只读属性以及非只读属性。但是我个人认为,虽然这样可以实现,但是会产生歧义,即在一个类中提供两个不同版本的属性,破坏了类的一致性,所以我并不推荐这么做。

  接着,要说说编写属性的时候,需要注意些什么,我个人认为有如下两点大的方面。

  第一个就是编写属性get部分的时候,如果当前属性的类型是引用类型的话,且不想通过属性来修改局部成员的话,最好返回局部成员的copy,而不是成员本身。

  例如:

public class class1
{
 string _data;
 public class1( string data )
 {
  _data = data;
 }

 public string Data
 {
  get{ return _data;}
  set{ _data = value;}
 }
}

public class class2
{
 private class1 myClass1 = null;
 public class1 Class1
 {
  get{ return myClass1; }
 }

 public string Data
 {
  get{ return myClass1.Data;}
 }

 public class2( string data )
 {
  myClass1 = new class1( data );
 }
}
  如果按照如上所写,那么class2对象可以通过Class1.Data属性访问和修改局部成员myClass1某些值,这样就可以修改了myClass2的私有成员myClass1的值,即会产生潜在错误。

  例如:

class1 myClass1 = myClass2.Class1;
myClass1.Data = "test2";
  如何避免这类错误呢,那么首先需要修改Class1属性的编写,其次在class1类需要提供Clone函数或者其他copy函数,具体如下:

public class class1:ICloneable
{
 string _data;
 public class1( string data )
 {
  _data = data;
 }

 public string Data
 {
  get{ return _data;}
  set{ _data = value;}
 }

 #region ICloneable Members

 public object Clone()
 {
  // TODO: Add class1.Clone implementation
  return new class1( _data );
 }
 #endregion
}

public class class2
{
 private class1 myClass1 = null;
 public class1 Class1
 {
  get{ return myClass1.Clone() as class1; }
 }

 public string Data
 {
  get{ return myClass1.Data;}
 }

 public class2( string data )
 {
  myClass1 = new class1( data );
 }
}
  第二个需要注意的是编写属性set部分的时候,这里需要对参数进行有效性检查。因为属性是外界修改类的私有成员的入口,为了避免因为私有成员不正确而产生的错误,所以在进行属性set的时候要进行有效性检查,从而保证私有成员对于整个类来说是有效的。

  那么在实际应用当中,与属性密切相关的就是实现两个窗体之间数据访问,这可能是写WinForm程序最基本的。不过很遗憾的是,网上在回答此类问题的时候,很多人都建议用第一种方法来解决。

时间: 2025-01-27 02:15:10

C#箴言之用属性来访问类的私有成员的相关文章

C++中用#define访问类的私有成员

我们知道,类的私有成员在类的外部是不能被访问的. 例如有下面的这个简单的类: class ClxECS{private:int iPrivate;}; 那么下面的这个函数是不能通过编译的: void ECS_test(){ClxECS lx;lx.iPrivate = 13; cout << lx.iPrivate << endl;} 但是,我们并不是没有办法来访问类的私有成员. 其实,方法很简单,只要在类的声明前面加上如下一行代码就行了: #define private publ

编程-C++数组作为类的私有成员求助

问题描述 C++数组作为类的私有成员求助 定义一个Employee类,其中包括姓名.街道地址.城市和邮编等属性,以及changeName()和display()等函数.display()显示姓名.街道地址.城市和邮编等属性,changeName()改变对象的姓名属性. 在employee.h文件中定义Employee类.Employee类具有姓名.街道地址.城市和邮编等私有数据成员,都可以用字符型数组来表示,在成员函数中,构造函数用来初始化所有成员数组,对字符数组的赋值可以使用字符串拷贝函数st

java 内部类-在外部类中为什么可以直接访问内部类的私有成员?

问题描述 在外部类中为什么可以直接访问内部类的私有成员? 在外部类中为什么可以直接访问内部类的私有成员?比如,root=root.next,这条语句出现在外部类的方法中,这里,root是定义在外部类中的一个内部类对象,而next是root对象的一个私有属性,为什么可以这样写? 解决方案 内部类是一个特例,相当于友元类(java本身没有友元这个概念) 从语法的角度来说,这样做是方便的.封装性主要是避免调用者随意操作对象的私有成员,调用他们不知道的代码引起问题.但是你定义了外部类,显然内部类也是你定

[C#] 常用工具类——应用程序属性信息访问类

using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace Utils { /// <summary> /// <para> </para> /// 常用工具类--应用程序属性信息访问类 /// <para> -------------------------------------------</para&g

c++-C++ 类内函数以引用作为返回值(例如返回类内私有成员变量)

问题描述 C++ 类内函数以引用作为返回值(例如返回类内私有成员变量) 主函数中用参数a接受这个返回值,那么对a的改变会引起私有成员变量的改变吗? 解决方案 返回了引用就可以修改它指向的变量.这种方式通过函数来达到修改内部变量的方式.从而改变封装的行为.不过一般要清楚这么做的目的 解决方案二: 作为引用的变量,相当于一个别名:原理上是指向同一块内存,因此对引用变量的修改就会导致原始变量跟着修改. 解决方案三: 同一个对象当然会改变.通过共有方法将私有成员传出来是很常见的做法,比如设计模式中的单例

java 内部类-为什么外部类能访问内部类的私有成员?

问题描述 为什么外部类能访问内部类的私有成员? 在学习单例时发现,外部类可以访问内部类的私有成员 如下面代码所示: //Initialization on Demand Holder class Singleton { private Singleton() { } private static class HolderClass { private final static Singleton instance = new Singleton(); } public static Single

c++-C++类的私有成员中定义了一个数组,怎么在构造函数中初始化这个数组?

问题描述 C++类的私有成员中定义了一个数组,怎么在构造函数中初始化这个数组? 写了这样一个类: class Base{ private: int r[MAX_SIZE]; int length; public: Base() { r[MAX_SIZE] = {1,2,0,5,8,9,7,3,6,4}; //编译的时候在这边出错 length = 10; } void swap_data(int,int); void Show(const Base &ob); friend int Bubble

c++-用友元类外函数调用类的私有成员,显示无法调用,是什么原因

问题描述 用友元类外函数调用类的私有成员,显示无法调用,是什么原因 #include #include using namespace std; class Date; class Time { private: int hour; int minute; int sec; public: Time(int h, int m, int s) :hour(h), minute(m), sec(s){} friend void display(Time & , Date & ); }; cla

C#中访问私有成员

首先我必须承认访问一个类的私有成员不是什么好做法.大家也都知道私有成员在外部是不能被访问的.而一个类中会存在很多私有成员:如私有字段.私有属性.私有方法.对于私有成员访问,可以套用下面这种非常好的方式去解决. private string name; public string Name { get { return name; } set { name = value; } } 但是有时候,源代码是别人的,你就不能修改源代码,只提供给你dll.或者你去维护别人的代码,源代码却有丢失.这样的情况