A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 陈君 于 2014-5-18 14:15 编辑

索引器和属性都有get和set而且都能通过它们对类里面的私有进行访问。那么它们的区别是什么呢?


BUG的具体情况在5楼

评分

参与人数 1技术分 +1 收起 理由
czwanglei + 1

查看全部评分

5 个回复

倒序浏览
这两个 其实这两个 不能进行区别。因为他们是一对好兄弟。

有两个类person:
    public class person
    {
      public string name;
    }
    public class person
    {
      public string Name{set;get;}
    }
    第一个类型的name属性未封装,其name属性直接通过public关键字暴露给系统中的其他类了,而第二个类型的name属性通过get set关键字进行了封装,get和set分别对应的是可读和可写,相当于如下代码:
    private string name;
    public string Name
    {
      get { return name; }
     set { name = value; }
  }
    简单说下区别吧:当实例化第一个“person”这个类时,系统在分配内存空间时对name属性直接分配内存,之后对name属性的操作也是直接操作内存中name属性所在的这个块;而当实例化第二个“人”类型时,系统会先分配一个叫name的private私有的内存空间,之后的读与写的操作都是通过Name这个public的类似于指针的东西来关联name进行,以此达到封装的目的,并且通过get和set关键字也可以控制可读还是可写。建议用后者,封装的好处可以参考相关文档,不再累述了~
    至于get和set的作用,除了控制读写之外还有其他的作用,举个简单的例子,比如说当我给Name赋值的时候想要先进行一些逻辑判断,就可以这样:
        private string name;
        public string Name
        {
            get { return name; }
            set
            {
             name = String.IsNullOrEmpty(value) ? "空" : value;
            }
        }

再举例说明下:
    假设类是一银行,既能存钱也能取钱
    Private Money;
    Private class bank()
    {
      get
      {
         return Money;
      }
      Set
      {
         Money=value;
      }
    }
    Money 就像银行里的自动取款机,你看不见里面的Money,但你能用set(存钱),用get(取钱)。Money是一个私有字段,是分装在类中的,类以外的程序不能直接访问的。C#中get、set用法中,类的set和get成员是外部程序访问类内部属性的唯一方法,就像你去银行取钱,你不能直接从银行的保险柜里拿钱,而是银行营业人员把钱取出来给你的。
    属性在调用者看来就像一个普通的变量,但作为类的设计者,你可以利用属性来隐藏你类中的一些字段,使外界只能通过属性来访问你的字段,你可以通过属性来限制外界对你的字段的存取,就利用get、set。如果想让用户随意存取你的字段,那么就实现get、set;如果只想让用户读取字段,就只实现get;若只想让用户写字段就只实现set。同时还可以在set和get中对用户传递的值进行一些验证工作,以确保你的字段将含有正确的值。
    Private int a;
    Public int index
    {
      get
      {
        return a;
      }
      set
      {
         If (value>0)
           a=value;
         else
           a=0;
      }
    }
    可看出,get、set用法中一则是隐藏组件或类内部的真是成员;
    二是用来建立约束的,比如,实现“有我没你”这种约束;
    三是用来响应属性变化事件,当属性变化是做某事,只要写在set方法里就行。
    当你想读出或写入属性的值时,存取标志限定了被实现的语句。用于读出属性的值的存取标志记为关键字get,而要修改属性的值的读写符标志记为set。

评分

参与人数 1技术分 +2 收起 理由
czwanglei + 2

查看全部评分

回复 使用道具 举报
C#中的索引器跟属性方法有点相似,属性方法既不是属性,也不是方法,它是没有参数的。
属性方法(也叫做访问方法)有两种get.set.  索引器跟属性不同的地方它的名一直都是this,并且它有参数,参数放在方括号中。
一个例子:通过索引器访问类中的数组(参考文档) 不是。可以用任意类型。

评分

参与人数 1技术分 +1 收起 理由
czwanglei + 1

查看全部评分

回复 使用道具 举报
方杰斌 发表于 2014-5-18 11:02
这两个 其实这两个 不能进行区别。因为他们是一对好兄弟。

有两个类person:

额,好像都在讲属性吧?那么索引器呢?
回复 使用道具 举报
陈君 发表于 2014-5-18 11:15
额,好像都在讲属性吧?那么索引器呢?

索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,使程序看起来更为直观,更容易编写。

1、索引器的定义

C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作。

定义索引器的方式与定义属性有些类似,其一般形式如下:

[修饰符] 数据类型 this[索引类型 index]

{

    get{//获得属性的代码}                                                

    set{ //设置属性的代码}

}

修饰符包括 public,protected,private,internal,new,virtual,sealed,override, abstract,extern.

数据类型是表示将要存取的数组或集合元素的类型。

索引器类型表示该索引器使用哪一类型的索引来存取数组或集合元素,可以是整数,可以是字符串;this表示操作本对象的数组或集合成员,可以简单把它理解成索引器的名字,因此索引器不能具有用户定义的名称。 例如:

class Z
{
        //可容纳100个整数的整数集
        private long[] arr = new long[100];
        //声明索引器
        public long this[int index]
        {
            get
            { //检查索引范围
                if (index < 0 || index >= 100)
                {
                    return 0;
                }
                else
                {
                    return arr[index];
                }
            }
            set
            {
                if (!(index < 0 || index >= 100))
                {
                    arr[index] = value;
                }
            }
   }

2、索引器的使用

通过索引器可以存取类的实例的数组成员,操作方法和数组相似,一般形式如下:

对象名[索引]

其中索引的数据类型必须与索引器的索引类型相同。例如:

Z  z=new  z();

z[0]=100;

z[1]=101;

Console.WriteLine(z[0]);

表示先创建一个对象z,再通过索引来引用该对象中的数组元素。

3、接口中的索引器

在接口中也可以声明索引器,接口索引器与类索引器的区别有两个:一是接口索引器不使用修饰符;二是接口索引器只包含访问器get或set,没有实现语句。访问器的用途是指示索引器是可读写、只读还是只写的,如果是可读写的,访问器get或set均不能省略;如果只读的,省略set访问器;如果是只写的,省略get访问器。

例如:

public interface IAddress

{

string this[int index]{get;set;}

string Address{get;set;}

string Answer();

}

表示所声明的接口IAddress包含3个成员:一个索引器、一个属性和一个方法,其中,索引器是可读写的。

4、索引器与属性的比较

索引器与属性都是类的成员,语法上非常相似。索引器一般用在自定义的集合类中,通过使用索引器来操作集合对象就如同使用数组一样简单;而属性可用于任何自定义类,它增强了类的字段成员的灵活性。

                     属        性                                             索  引  器

允许调用方法,如同公共数据成员

允许调用对象上的方法,如同对象是一个数组

可通过简单的名称进行访问

可通过索引器进行访问

可以为静态成员或实例成员

必须为实例成员

其get访问器没有参数

其get访问器具有与索引器相同的形参表

其set访问器包含隐式value参数

除了value参数外,其set访问器还具有与索引器相同的形参表

评分

参与人数 1技术分 +1 收起 理由
czwanglei + 1

查看全部评分

回复 使用道具 举报
整合了下各位朋友的回答和直接的实验。总结了篇博文出来。调试到最后的时候还发现了一个VS的BUG,不知道算不算。大家一起来看下。

今天突然看到索引器这个名词。原来索引器是类里的很特殊的一种成员。它的地位和属性很像。那我就来从属性说起,毕竟属性更常见点。

    private int num;  //字段

       public int Num//属性
       {
           get { return num; }
           set { num = value; }
       }

比如上面的代码,我们在类中定义了一个num字段。但是用了private把他封装了起来。那么外部就不能直接访问到这个字段了。但是当我们需要获得它的值或者是我们需要改变它的值的话应该怎么做呢?就是像上面这样利用一个属性来达到这样的目的。因为属性是public的。而且属性的set可以为我们保护字段设置一道防火墙。防止别人随意的设置字段的值。比如我们的num只能 是大于等于了的话,那么我们可以这样写

       private int num;//字段

       public int Num//属性
       {
           get { return num; }
           set
           {
               if (value >= 0)//只有value大于等于了才可以被赋值给num字段
                   num = value;
               else
                   num = 0;
           }
       }

这样就实现了属性对我们私有字段的保护了。

然后再来说说索引器。先看看索引器的格式

type this[itype argIndex]

{

get

{

.....

}

set

{

....

}

}

这里的type就是索引器的返回类型,比如Int,string等,但是必须要和你所希望操作的字段的类型一致。

看一下具体的程序

    private int[] intarr = new int[5]; //定义一个长度为5的int型数组来作为类的字段
       private int num;  //定义个给int型的字段

       public int Num   //属性
       {

get { return num; }
           set
           {
               if (value >= 0)
                   num = value;
               else
                   num = 0;
           }
       }
        public int[] Intarr //intarr这个字段的属性
        {
            get { return intarr; }
            set { intarr = value; }
        }
        public int this[int index] //intarr这个字段的索引器
        {
            get
            {
                return intarr[index];
            }
            set
            {
                intarr[index] = value;
            }
        }

然后我先用下intarr的属性看看能不能对这个数组赋值


我们发现不们不能直接这样用。我们只能传入一个数组才行。这样的话对应一个数组类型的字段来是,用属性显的很不方便。而且还有一个问题。如果我传入的不是长度为5的数组呢?它会报错嘛?如果长度是4呢?又会怎么样呢?




我们可以发现intarr的长度竟然变化了,它的长度会和传入的数组 的长度一模一样。这就不是我们希望看到的了。就好像每个人都被要求是一夫一妻的。但是一个不小心的操作造成了一夫多妻,或者可能连拥有妻子的权利都没有。所以用属性来作为一个数组型字段的接口的话不仅仅操作不方便,而且对数据的封装有很大的威胁。

然后我们来用下索引器试试。


我们可以看到,这里用索引器来操作数组型的字段变的很方便就像操作一个数组一样。那么如果我进行了次误操作比如shu1[5]=5;会发生什么呢?

这里我们程序还没有运行shu1[5]=3;这一句的时候,局部变量还都是可以看到的。但是如果我再点一次单步运行的话,程序就会直接结束,并且无视我设在下面的另一个断点。


可能有些人会觉得不可思议。觉得我乱编,但是事实就是这样,我一开始也无法接受。程序我也已经附上去了,不相信的朋友可以也去试试。我对这个现象的理解是由于我们误操作了这个字段,系统直接跳过了下面的程序。

我们可以这样验证下。


我们发现竟然label1竟然不是显示2.看来果然没有执行shu1[5]=3;下面的程序。虽然这样使用索引器的时候程序没直接报错让我们很失望,但是这可能是VS的一个BUG,系统其实是意识到了数组超长的问题的。只是没有报错。所以相比于使用属性的时候它偷偷的把我们的字段数组的长度给改变了。使用索引器还是更安全的。


总结:当被封装的字段是一个数组的时候,还是使用索引器好。操作方便而且更安全。但是VS的这个BUG还是很头疼的。


博文地址:
http://blog.sina.com.cn/s/blog_db23a6350101mjxw.html

5.jpeg (78.67 KB, 下载次数: 27)

5.jpeg

评分

参与人数 1技术分 +3 收起 理由
czwanglei + 3 写的很认真

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马