第八讲 索引器与操作符重载

南京邮电学院 李建忠(cornyfield@263.net)

索引


C#锐利体验

"Hello,World!"程序

C#语言基础介绍

Microsoft.NET平台基础构造

类与对象

构造器与析构器

方法

域与属性

索引器与操作符重载

数组与字符串

特征与映射

COM互操作 非托管编程与异常处理

用C#编织未来--C#编程模型概述

索引器

索引器(Indexer)是C#引入的一个新型的类成员,它使得对象可以像数组那样被方便,直观的引用。索引器非常类似于我们前面讲到的属性,但索引器可以有参数列表,且只能作用在实例对象上,而不能在类上直接作用。下面是典型的索引器的设计,我们在这里忽略了具体的实现。

class MyClass
{
public object this [int index]
{
get
{
// 取数据
}
set
{
// 存数据
}
}
}

索引器没有像属性和方法那样的名字,关键字this清楚地表达了索引器引用对象的特征。和属性一样,value关键字在set后的语句块里有参数传递意义。实际上从编译后的IL中间语言代码来看,上面这个索引器被实现为:

class MyClass
{
public object get_Item(int index)
{
// 取数据
}
public void set_Item(int index, object value)
{
//存数据
}
}

由于我们的索引器在背后被编译成get_Item(int index)和set_Item(int index, object value)两个方法,我们甚至不能再在声明实现索引器的类里面声明实现这两个方法,编译器会对这样的行为报错。这样隐含实现的方法同样可以被我们进行调用,继承等操作,和我们自己实现的方法别无二致。通晓C#语言底层的编译实现为我们下面理解C#索引器的行为提供了一个很好的基础。

和方法一样,索引器有5种存取保护级别,和4种继承行为修饰,以及外部索引器。这些行为同方法没有任何差别,这里不再赘述。唯一不同的是索引器不能为静态(static),这在对象引用的语义下很容易理解。值得注意的是在覆盖(override)实现索引器时,应该用base[E]来存取父类的索引器。

和属性的实现一样,索引器的数据类型同时为get语句块的返回类型和set语句块中value关键字的类型。

索引器的参数列表也是值得注意的地方。“索引”的特征使得索引器必须具备至少一个参数,该参数位于this关键字之后的中括号内。索引器的参数也只能是传值类型,不可以有ref(引用)和out(输出)修饰。参数的数据类型可以是C#中的任何数据类型。C#根据不同的参数签名来进行索引器的多态辨析。中括号内的所有参数在get和set下都可以引用,而value关键字只能在set下作为传递参数。

下面是一个索引器的具体的应用例子,它对我们理解索引器的设计和应用很有帮助。

using System;
class BitArray
{
int[] bits;
int length;
public BitArray(int length)
{
if (length < 0)
throw new ArgumentException();
bits = new int[((length - 1) >> 5) + 1];
this.length = length;
}
public int Length
{
get { return length; }
}
public bool this[int index]
{
get
{
if (index < 0 || index >= length)
throw new IndexOutOfRangeException();
else
return (bits[index >> 5] & 1 << index) != 0;
}
set
{
if (index < 0 || index >= length)
throw new IndexOutOfRangeException();
else if(value)
bits[index >> 5] |= 1 << index;
else
bits[index >> 5] &= ~(1 << index);
}
}
}
class Test
{
static void Main()
{
BitArray Bits=new BitArray(10);
for(int i=0;i<10;i++)
Bits[i]=(i%2)==0;
Console.Write(Bits[i]+"  ");
}
}

编译并运行程序可以得到下面的输出:

  • True False True False True False True False True False

上面的程序通过索引器的使用为用户提供了一个界面友好的bool数组,同时又大大降低了程序的存储空间代价。索引器通常用于对象容器中为其内的对象提供友好的存取界面--这也是为什么C#将方法包装成索引器的原因所在。实际上,我们可以看到索引器在.NET Framework类库中有大量的应用。

操作符重载

操作符是C#中用于定义类的实例对象间表达式操作的一种成员。和索引器类似,操作符仍然是对方法实现的一种逻辑界面抽象,也就是说在编译成的IL中间语言代码中,操作符仍然是以方法的形式调用的。在类内定义操作符成员又叫操作符重载。C#中的重载操作符共有三种:一元操作符,二元操作符和转换操作符。并不是所有的操作符都可以重载,三种操作符都有相应的可重载操作符集,列于下表:

  • 一元操作符 + - ! ~ ++ -- true false
    二元操作符 + - * / % & | ^ << >> == != > < >= <=
    转换操作符 隐式转换()和显式转换()

重载操作符必须是public和static 修饰的,否则会引起编译错误,这在操作符的逻辑语义下是不言而喻的。父类的重载操作符会被子类继承,但这种继承没有覆盖,隐藏,抽象等行为,不能对重载操作符进行virtual sealed override abstract修饰。操作符的参数必须为传值参数。我们下面来看一个具体的例子:

using System;class Complex{double  r, v;  //r+ v ipublic Complex(double r, double v){this.r=r;this.v=v;}public static Complex operator +(Complex a, Complex b){return new Complex(a.r+b.r, a.v+b.v);}public static Complex operator -(Complex a){return new Complex(-a.r,-a.v);}public static Complex operator ++(Complex a){double r=a.r+1;double v=a.v+1;return new Complex(r, v);}public void Print(){Console.Write(r+" + "+v+"i");}}class Test{public static void Main(){Complex a=new Complex(3,4);Complex b=new Complex(5,6);Complex c=-a;c.Print();Complex d=a+b;d.Print();a.Print();Complex e=a++;a.Print();e.Print();Complex f=++a;a.Print();f.Print();}}

编译程序并运行可得到下面的输出:

  • -3 + -4i 8 + 10i 3 + 4i 4 + 5i 3 + 4i 5 + 6i 5 + 6i

我们这里实现了一个“+”号二元操作符,一个“-”号一元操作符(取负值),和一个“++”一元操作符。注意这里,我们都没有对传进来的参数作任何改变--这在参数是引用类型的变量是尤其重要,虽然重载操作符的参数只能是传值方式。而我们在返回值时,往往需要“new”一个新的变量--除了true和false操作符。这在重载“++”和“--” 操作符时尤其显得重要。也就是说我们做在a++时,我们将丢弃原来的a值,而取代的是新的new出来的值给a! 值得注意的是e=a++或f=++a中e的值或f的值根本与我们重载的操作符返回值没有一点联系!它们的值仅仅是在前置和后置的情况下获得a的旧值或新值而已!前置和后置的行为不难理解。

操作符重载对返回值和参数类型有着相当严格的要求。一元操作符中只有一个参数。操作符“++”和“--”返回值类型和参数类型必须和声明该操作符的类型一样。操作符“+ - ! ~”的参数类型必须和声明该操作符的类型一样,返回值类型可以任意。true和false操作符的参数类型必须和声明该操作符的类型一样,而返回值类型必须为bool,而且必须配对出现--也就是说只声明其中一个是不对的,会引起编译错误。参数类型的不同会导致同名的操作符的重载--实际上这是方法重载的表现。

二元操作符参数必须为两个,而且两个必须至少有一个的参数类型为声明该操作符的类型。返回值类型可以任意。有三对操作符也需要必须配对声明出现,它们是“==”和“!=”,“>”和“<”,“>=”和“<=”。需要注意的是两个参数的类型不同,虽然类型相同但顺序不同都会导致同名的操作符的重载。

转换操作符为不同类型之间提供隐式转换和显式转换,主要用于方法调用,转型表达和赋值操作。转换操作符对其参数类型(被转换类型)和返回值类型(转换类型)也有严格的要求。参数类型和返回值类型不能相同,且两者之间必须至少有一个和定义操作符的类型相同。转换操作符必须定义在被转换类型或转换类型任何其中一个里面。不能对系统定义过的转换操作进行重新定义。两个类型也都不能是object或接口类型,两者之间不能有直接或间接的继承关系--这三种情况系统已经默认转换。我们来看一个例子:

using System;public struct Digit{byte value;public Digit(byte value){if (value < 0 || value > 9)throw new ArgumentException();this.value = value;}public static implicit operator byte(Digit d){return d.value;}public static explicit operator Digit(byte b){return new Digit(b);}}

上面的例子提供了Digit类型和byte类型之间的隐式转换和显式转换。从Digit到byte的转换为隐式转换,转换过程不会因为丢失任何信息而抛出异常。从byte到Digit的转换为显式转换,转换过程有可能因丢失信息而抛出异常。实际上这也为我们揭示了什么时候声明隐式转换,什么时候声明显示转换的设计原则。不能对同一参数类型同时声明隐式转换和显式转换。隐式转换和显式转换无需配对使用--虽然C#推荐这样做。

实际上可以看到,对于属性,索引器和操作符这些C#提供给我们的界面操作,都是方法的某种形式的逻辑抽象包装,它旨在为我们定义的类型的用户提供一个友好易用的界面--我们完全可以通过方法来实现它们实现的功能。理解了这样的设计初衷,我们才会恰当,正确地用好这些操作,而不致导致滥用和错用。

转载于:https://www.cnblogs.com/Dicky/archive/2007/05/09/740782.html

C#锐利体验-第八讲 索引器与操作符重载(转)相关推荐

  1. C#锐利体验-第五讲 构造器与析构器(转)

    第五讲 构造器与析构器 南京邮电学院 李建忠(cornyfield@263.net) 索引   C#锐利体验   "Hello,World!"程序   C#语言基础介绍   Mic ...

  2. C#拾遗系列(4):索引器

    1. 概述 索引器允许类或结构的实例就像数组一样进行索引.索引器类似于属性,不同之处在于它们的访问器采用参数.索引器在语法上方便您创建客户端应用程序可将其作为数组访问的类.结构或接口.索引器经常是在主 ...

  3. c#初学-索引器get和set的使用(泛型类)

    索引器允许类或结构的实例就像数组一样进行索引.索引器类似于属性,不同之处在于它们的访问器采用参数. 在下面的示例中,定义了一个泛型类,并为其提供了简单的 get 和 set 访问器方法(作为分配和检索 ...

  4. c#索引器介绍|C#索引器写法|c#索引器例子

    索引器就是特殊的属性,以下是最简单的索引器例子 就是给定索引编号去找索引编号对应的值,类似属性的数组 public string this[string configName]         {   ...

  5. C# 索引器使用总结

    1.索引器(Indexer): 索引器允许类或者结构的实例按照与数组相同的方式进行索引.索引器类似于属性,不同之处在于他们的访问采用参数. 最简单的索引器的使用  代码如下: /// <summ ...

  6. C# 索引器的简单例子

    这个例子是使用索引器简单模仿一个string数组. 因为索引器本身就比较像数组,下面是关于索引器的概述: 索引器使得对象可按照与数组相似的方法进行索引. get 访问器返回值.set 访问器分配值. ...

  7. C#索引器-索引器的定义

    索引器定义类似于属性,但其功能与属性并不相同.索引器提供一种特殊的方法编写get和set访问器.属性可以像访问字段一样访问对象的数据,索引器可以使用户像访问数组一样访问类成员.定义索引器的语法如下: ...

  8. 【转】C#索引器-索引器的定义

    索引器定义类似于属性,但其功能与属性并不相同.索引器提供一种特殊的方法编写get和set访问器.属性可以像访问字段一样访问对象的数据,索引器可以使用户像访问数组一样访问类成员.定义索引器的语法如下: ...

  9. C#索引器与属性的区别、索引器的具体使用

    C#中索引器和属性类似,但也有一些区别: a.    属性可以调用方法就如同他们是公共数据成员,而索引器可以调用对象上得方法,如同对象是一个数组. b.    属性可以通过简单的名称访问, 而索引器需 ...

最新文章

  1. 索引是建的越多越好吗?
  2. SD-WAN应用可见性的流量对称
  3. SQLite学习手册(锁和并发控制)
  4. Python中的startswith和endswith函数使用实例
  5. 工具|渗透利器之CrimeFlare、waf绕过脚本分享
  6. python的wxpy库_【微信辅助】吃鸡再也不怕了,Python用wxpy实现微信自动回复
  7. java 任务栏程序_如何在任务栏显示java程序图标
  8. 基于python实现微信公众号爬虫_基于Python实现微信公众号爬虫进行数据分析
  9. 硬盘盒刷固件使其不休眠
  10. 一文了解plc编程、电脑编程、手机APP编程、组态编程、云编程(上)
  11. phpStudy的建站与踩坑,与微信公众号对接
  12. java计算指定日期的上个月
  13. 前端常见问题以及处理方式 - - - (九) ES6中的set和map(map篇)
  14. 众数和中位数-华为OD
  15. pytest 测试开发 —— 上手 pytest 实现自动化测试
  16. C++11 IOC容器
  17. Top-k高效用项集挖掘_学习笔记(三) TKO
  18. 震撼收藏:《歌声飘过30年--第五场:和谐中国(2003年-2008年)信息时代歌曲风格的形成》,场场精彩!!!
  19. 浅谈互联网+、挑战杯等创新创业类比赛
  20. ChromeDriver/Selenium/Python浏览器自动化初体验

热门文章

  1. python天天向上续2_2019/2/12 Python今日收获
  2. 电视机原理图_电工电气,如何看电气原理图和接线图,如何设计图纸?
  3. 接受与返回json数据
  4. P2280 [HNOI2003]激光炸弹
  5. in-place数据交换
  6. bzoj 3668 数位DP
  7. hive处理日志,自定义inputformat
  8. 最简洁的js鼠标拖曳效果【原】
  9. ubuntu+eclipse+svn
  10. java callable用法_在Java中使用Callable和Future