《Effective C#》Item 1:用属性来访问类的私有成员

原创 2006年06月04日 17:45:00

在程序中,难免要访问某个对象的私有成员。那么以前实现这类功能的方法有两种,第一种方法最简单,就是把成员访问符从“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#写程序,一提到属性,大家都会编写。其实在属性中,可以产生很多应用,接着来就分别说明。

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

public ReturnValueType this[ ParType1 parValue1, ParType2 parValue2]

{

    get{...}

    set{...}

}

 

2.  可以给属性操作加上互斥锁,以防止多线程操作时而产生的并发错误,具体如下。

public string Name

{

    get

    {

        lock(this)

        {

            return strName;

        }

    }

    set

    {

        lock(this)

        {

            strName = value;

        }

    }

}

 

3.  书上还提到属性的其他应用,例如:通过接口来实现在一个类中同时提供只读属性以及非只读属性。但是我个人认为,虽然这样可以实现,但是会产生歧义,即在一个类中提供两个不同版本的属性,破坏了类的一致性,所以我并不推荐这么做。

 

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

第一个就是编写属性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程序最基本的。不过很遗憾的是,网上在回答此类问题的时候,很多人都建议用第一种方法来解决。为此,我也专门写了一篇文章,详情参看:

http://blog.csdn.net/knight94/archive/2006/03/18/628285.aspx

 

可能之前的文章没有这篇说的那么透彻,所以我也希望大家在看完之后,能真正的把属性应用到程序当中。

如何快速生成100万不重复的8位编号

最近在论坛看到有人问,如何快速生成100万不重复的8位编号,对于这个问题,有几点是需要注意的:1.    如何生成8位随机数,生成的数越随机,重复的可能性当然越小2.    控制不重复3.    考虑...
  • zjcxc
  • zjcxc
  • 2006年08月20日 11:28
  • 20667

初探 SQL Server 2008 中的更改跟踪与变更数据捕获

1                概述1.1        更改跟踪1.2        变更数据捕获1.3        比较更改跟踪和变更数据捕获2                使用2.1   ...
  • zjcxc
  • zjcxc
  • 2009年03月10日 11:02
  • 14088

用属性来访问类的私有成员

在程序中,难免要访问某个对象的私有成员。那么以前实现这类功能的方法有两种,第一种方法最简单,就是把成员访问符从“private”改为“public”即可;而另一个就是提供公有的成员访问函数来进行访问。...
  • wnety
  • wnety
  • 2011年06月22日 15:09
  • 442

Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field)

http://www.cnblogs.com/WuCountry/archive/2007/02/10/646756.html Effective C# 原则1:尽可能的使用属性(property),...

[Effective Java Distilled] Item 3 通过使用私有构造方法或者枚举类型来强化单例属性

关于Effective Java Distilled: 《Effective Java》这本书我断断续续的读了近两遍,里面的内容挺有深度,对提高工程代码质量也非常有帮助。我打算慢慢的整理出来一个...

[Effective Java Distilled] Item 3 通过使用私有构造方法或者枚举类型来强化单例属性

关于Effective Java Distilled: 《Effective Java》这本书我断断续续的读了近两遍,里面的内容挺有深度,对提高工程代码质量也非常有帮助。我打算慢慢的整理出来一个...

【Effective Java】Ch2_创建销毁对象:Item3_通过私有构造函数或枚举类型来强化Singleton属性

所谓Singleton,是指仅能被实例化一次的类。Singleton通常代表本质上唯一的系统组件,例如窗口管理器或文件系统。让一个类成为Singleton就无法为Singleton替换模拟实现,除非它...

用简单的方法来访问GObject对象的私有属性

这里只用到了最简单的方法,给我的感觉就是使用g_object_get/set函数来完成对已经安装的属性的存取工作。 目前我们还没有涉及到‘信号’这个NB的东西,连interface'接口'是什么,我...
  • xbl1986
  • xbl1986
  • 2011年08月26日 15:23
  • 775

Java反射机制,通过对象访问调用类的私有成员属性或者方法

Class是对类的抽象(每个类对应一份字节码)。一般情况下,比如A a = new A();直接通过a对象调用方法即可。但是在有些场景下,这样是做不到的,比如类名A是通过参数传递过来的,这时候你就无法...

C#属性详解(c# 通过公共属性访问私有成员 优点)

C#属性是域的扩展(即通常说的成员变量或字段等)它配合C#中的域(字段)使用,使之构造一个安全的应用程序,为什么说通过属性来替代域会提高应用程序的安全呢?     原因就在于C#属性通过访问器(...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:《Effective C#》Item 1:用属性来访问类的私有成员
举报原因:
原因补充:

(最多只允许输入30个字)