参考书:《 visual C# 从入门到精通》
第三部分 用C#定义可扩展类型
第16章 使用索引器
文章目录
16.1 什么是索引器
我们通常用int
来存储整数值。int
内部将值存储为32位,每一位为0或1。
C#提供以下操作符来访问和操纵int
中单独的二进制位。
- ~(NOT)操作符:执行按位求补
- <<(左移位)操作符:二元操作符,向左移位,如
204<<2
,将返回48 - |(OR)操作符:二元操作符,执行按位OR
- &(AND)操作符:执行按位AND
- ^(XOR)操作符:执行按位XOR(异或)
综合起来我们就可以用一个表达式来判断一个int
中单独的位的值。
不如对于类型为int的名为
bit`的变量判断位置5(右数第6位)的二进制位是1还是0:
(bits&(1<<5))!=0
又如将位置6的位设为0:bits &=~(1<<5)
将位置6的位设为1:bits|=~(1<<5)
这些例子语法上都没问题的,但看起来很不直观,会提升维护难度。
16.1.2 同一个例子改用索引器
前面我们给出的实例其最佳解决方案应该是将int
作为一个32元素的布尔数组,这样我们就可以直接的对每一位进行操作了。但我们并不能直接对int
进行操作,所以我们要新建一种类型,它在行为、外观、用法上都类似于bool
数组,但它是用int
实现的。为此定义一个索引器
。如:
struct IntBits{
private int bits;
public IntBits(int initialBitValue){
bits=initialBitValue;
}
//在这里写索引器
}
定义索引器要一种兼具属性和数组特征的记号法。由关键字this
引入,this
前是返回类型,之后是方括号,方括号中指定索引器的索引的类型和形参。如下:
struct IntBits{
...;
public bool this [int index]{
get{
return (bits&(1<<index))!=0;
}
set{
if(value)//value为true或false,指定为1或0
bits|=(1<<index);
else
bits&=~(1<<index);
}
}
}
注意:
- 索引器不是方法,他没有一对包含参数的圆括号
- 所有索引器都用
this
取代方法名 - 和属性一样,索引器包含
get
和set
两个访问器 - 索引器声明中指定的
index
将为get
和set
访问器所用
这样一来我们就可以使用方括号计数法:
int adapted=126;
IntBits bits=new IntBits(adapted);
bool peek=bits[6];
bits[0]=true;
bits[3]=false;
16.1.3 理解索引器的访问器
对索引器读取或者访问时其实是在对get
和set
访问器进行调用。
16.1.4 对比索引器和数组
索引器和数组语法上很相似,但二者存在重要区别:
- 索引器可以使用非数值下标,但数组只能使用整数下标
- 索引器能重载,但数组不行
- 索引器不能作为
ref
或out
的参数使用,但数组可以
16.2 接口中的索引器
可以在接口中声明索引器:
interface IRawInt{
bool this[int index]{get;set;}
}
16.3 在Windows应用程序中使用索引器
最后我们又来完成一个实例。
创建一个空白应用项目,先拖放控件如下图所示:
其中显然有3个TextBlock
,3个TextBox
,2个Button
,底下是CommandBar
(命令栏),在CommandBar
的属性窗口的公共
|Content
栏点击新建
,找到Button
,大致上这样布局。注意3个TextBox
都要命名,如最上面的名为message
,下面两个命名为name
和phoneName
。
3个Button
控件设置好click
事件的处理方法。
源文件代码如下:
Names.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C_16_3
{
class Name
{
private readonly string text;
public string Text
{
get { return this.text; }
}
public Name()
{
this.text = "";
}
public Name(string name)
{
this.text = name;
}
}
}
PhoneNumber.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C_16_3
{
class PhoneNumber
{
private readonly string text;
public string Text
{
get { return this.text; }
}
public PhoneNumber()
{
this.text = "";
}
public PhoneNumber(string phone)
{
this.text = phone;
}
}
}
PhotoBook.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C_16_3
{
sealed class PhoneBook
{
private Name [] names;
private PhoneNumber[] phonenames;
private int length = 10;
private int index = 0;
public Name this [PhoneNumber number]
{
get
{
//int i = Array.IndexOf(this.phonenames, number);
int i = -1;
for(int x = 0; x < this.index; ++x)
{
if (this.phonenames[x].Text == number.Text)
{
i = x;
break;
}
}
if (i != -1)
return this.names[i];
else
return new Name();
}
}
public override string ToString()
{
string texts = "";
for(int x = 0; x < this.index; ++x)
{
texts += $"{x}->{this.names[x].Text}->{this.phonenames[x].Text}\n";
}
return texts;
}
public PhoneBook()
{
names = new Name[length];
phonenames = new PhoneNumber[length];
}
public void enlargeIfFull()
{
if (this.index == this.length)
{
this.length *= 2;
Name[] newNames = new Name[this.length];
PhoneNumber[] newPhoneNumber = new PhoneNumber[this.length];
for(int x = 0; x < names.Length; ++x)
{
newNames[x] = names[x];
newPhoneNumber[x] = phonenames[x];
}
names = newNames;
phonenames = newPhoneNumber;
}
}
public void addItem(string name,string phoneNumber)
{
names[index] = new Name(name);
phonenames[index] = new PhoneNumber(phoneNumber);
++index;
}
public string getIndex()
{
return index.ToString();
}
public PhoneNumber this[Name name]
{
get
{
//int i = Array.IndexOf(this.names, name);
int i = -1;
for (int x = 0; x < this.index; ++x)
{
if (this.names[x].Text == name.Text)
{
i = x;
break;
}
}
if (i != -1)
return this.phonenames[i];
else
return new PhoneNumber();
}
}
}
}
最后,MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板
namespace C_16_3
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private PhoneBook phoneBook = new PhoneBook();
private void findByNameClick(object sender, RoutedEventArgs e)
{
string text = name.Text;
if (!String.IsNullOrEmpty(text))
{
Name personName = new Name(text);
PhoneNumber personPhoneNumber = this.phoneBook[personName];
phoneNumber.Text = String.IsNullOrEmpty(personPhoneNumber.Text) ? "Not Found"
: personPhoneNumber.Text;
}
}
private void findByNumberClick(object sender, RoutedEventArgs e)
{
string text = phoneNumber.Text;
if (!String.IsNullOrEmpty(text))
{
PhoneNumber personsPhoneNumber = new PhoneNumber(text);
Name personsName = this.phoneBook[personsPhoneNumber];
name.Text = String.IsNullOrEmpty(personsName.Text) ? "NotFound"
: personsName.Text;
}
}
private void addClick(object sender, RoutedEventArgs e)
{
string personName = name.Text;
string personPhoneNumber = phoneNumber.Text;
if (!(String.IsNullOrEmpty(personName) || String.IsNullOrEmpty(personPhoneNumber))){
phoneBook.enlargeIfFull();
phoneBook.addItem(personName, personPhoneNumber);
//message.Text += $"{phoneBook.getIndex()}--{name.Text}--{phoneNumber.Text}\n";
}
message.Text = this.phoneBook.ToString();
}
}
}
运行效果如下: