当前位置:
首页 > 编程开发 > Objective-C编程 >
-
c#泛型秘诀6
制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
4.9 使用泛型创建只读集合
问题
您希望类中的一个集合里的信息可以被外界访问,但不希望用户改变这个集合。
解决方案
使用ReadOnlyCollection<T>包装就很容易实现只读的集合类。例子如,Lottery类包含了中奖号码,它可以被访问,但不允许被改变:
public class Lottery
{
// 创建一个列表.
List<int> _numbers = null;
public Lottery()
{
// 初始化内部列表
_numbers = new List<int>(5);
// 添加值
_numbers.Add(17);
_numbers.Add(21);
_numbers.Add(32);
_numbers.Add(44);
_numbers.Add(58);
}
public ReadOnlyCollection<int> Results
{
// 返回一份包装后的结果
get { return new ReadOnlyCollection<int>(_numbers); }
}
}
Lottery有一个内部的List<int>,它包含了在构造方法中被填的中奖号码。有趣的部分是它有一个公有属性叫Results,通过返回的ReadOnlyCollection<int>类型可以看到其中的中奖号码,从而使用户通过返回的实例来使用它。
如果用户试图设置集合中的一个值,将引发一个编译错误:
Lottery tryYourLuck = new Lottery();
// 打印结果.
for (int i = 0; i < tryYourLuck.Results.Count; i++)
{
Console.WriteLine("Lottery Number " + i + " is " + tryYourLuck.Results[i]);
}
// 改变中奖号码!
tryYourLuck.Results[0]=29;
//最后一行引发错误:// Error 26 // Property or indexer
// 'System.Collections.ObjectModel.ReadOnlyCollection<int>.this[int]'
// cannot be assigned to -- it is read only
讨论
ReadOnlyCollection的主要优势是使用上的灵活性,可以在任何支持IList或IList<T>的集合中把它做为接口使用。ReadOnlyCollection还可以象这样包装一般数组:
int [] items = new int[3];
items[0]=0;
items[1]=1;
items[2]=2;
new ReadOnlyCollection<int>(items);
这为类的只读属性的标准化提供了一种方法,并使得类库使用人员习惯于这种简单的只读属性返回类型。
阅读参考
查看MSDN文档中的“IList”和“Generic IList”主题。
4.10 使用相应的泛型版本替换Hashtable
问题
您希望通过使用相应的泛型版本替换所有Hashtable来增强应用程序性能并使得代码更为易读。当您发现这些数据结构中存放结构体和值类型会导致装箱/拆箱操作,这就变得非常有必要了。
解决方案
替换所有已存在的System.Collections.Hashtable类为速度更快的System.Collections.Generic.Dictionary泛型类。
这有一个使用System.Collections.Hashtable对象的简单例子:
public static void UseNonGenericHashtable()
{
// 创建并填充一个Hashtable.
Hashtable numbers = new Hashtable();
numbers.Add(1, "one"); // 键会导致装箱操作
numbers.Add(2, "two"); // 键会导致装箱操作
// 在Hashtable显示所有的键/值对.
// 在每次迭代中都会因为键导致一个拆箱操作
foreach (DictionaryEntry de in numbers)
{
Console.WriteLine("Key: " + de.Key + "tValue: " + de.Value);
}
numbers.Clear();
}
下面是相同的代码使用了System.Collections.Generic.Dictionary<T,U>对象:
public static void UseGenericDictionary()
{
// 创建并填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示字典中的所有键值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
numbers.Clear();
}
讨论
对于应用程序中简单的Hashtable实现,这种替换将十分容易。但有些地方需要注意,如泛型Dictionary类没有实现ICloneable接口,而Hashtable类实现了。
表4-2显示了两个类中的等价成员:
表4-2 Hashtable和泛型Dictionary类的等价成员
表4-2中,并非所有的Hashtable和Dictionary的成员都一一对应。我们从属性开始,注意,只有Count,Keys,Values和Item属性在两个类中都存在。为了弥补Dictionary中缺少的属性,需要把它转化为IDictionary类型。下面的代码演示了如果进行这些转换以获得缺少的属性:
Dictionary<int, string> numbers = new Dictionary<int, string>();
Console.WriteLine(((IDictionary)numbers).IsReadOnly);
Console.WriteLine(((IDictionary)numbers).IsFixedSize);
Console.WriteLine(((IDictionary)numbers).IsSynchronized);
Console.WriteLine(((IDictionary)numbers).SyncRoot);
注意,由于缺少返回一个泛型字典同步版本的代码,IsSynchronized属性将总是返回false。SyncRoot属性在被调用时总是返回相同的对象。实际上这个属性返回的是this指针。微软已经决定移除泛型集合类的创建同步包装的功能。
做为替代,他们推荐使用lock关键字锁住整个集合或其他同步对象类型以满足您的需求。
因为在泛型字典类中也缺少了克隆方法(实际是是因为这个类没有实现ICloneable接口),您可以转而使用重载的构造方法来接收一个IDictionary<T,U>类型:
// 创建并填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示原字典的键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Original Key: " + kvp.Key + "tValue: " + kvp.Value);
}
// 克隆字典对象.
Dictionary<int, string> clonedNumbers = new Dictionary<int, string>(numbers);
// 显示克隆字典中的键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Cloned Key: " + kvp.Key + "tValue: " + kvp.Value);
}
还有两个Dictionary类中缺少的方法:Contains和CopyTo方法。Contains方法的功能在Dictionary类中很容易被实现。在Hashtable类中,Cintains方法和ContainsKey方法有相同的行为,因此您可以在Dictionary类中简单地使用ContainsKey方法来模拟Hashtable类中的Contains方法:
// 创建和填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
Console.WriteLine("numbers.ContainsKey(1) == " + numbers.ContainsKey(1));
Console.WriteLine("numbers.ContainsKey(3) == " + numbers.ContainsKey(3));
CopyTo方法也很容易在Dictionary类中被模拟,但需要做一些额外的工作:
// 创建和填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示字典中的所有键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
// 创建对象数组来拷贝字典对象中的信息.
KeyValuePair<int, string>[] objs = new KeyValuePair<int,
string>[numbers.Count];
// 调用字典中的CopyTo方法
// 把字典中的所有键/值对对象拷贝到objs中
((IDictionary)numbers).CopyTo(objs, 0);
// 显示objs[]中的所有键/值对.
foreach (KeyValuePair<int, string> kvp in objs)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
调用Dictionary对象中的CopyTo方法需要创建一个KeyValuePair<T,U>对象数组,它用于在CopyTo方法被调用之后,控制字典对象中的所有KeyValuePair<T,U>对象。接下来numbers字典对象被转换为IDictionary类型以调用CopyTo方法。一旦CopyTo方法被调用,objs数组将包含原numbers对象中的所有KeyValuePair<T,U>对象。注意objs数组迭代时使用了foreach循环,这点和numbers对象是相同的。
4.9 使用泛型创建只读集合
问题
您希望类中的一个集合里的信息可以被外界访问,但不希望用户改变这个集合。
解决方案
使用ReadOnlyCollection<T>包装就很容易实现只读的集合类。例子如,Lottery类包含了中奖号码,它可以被访问,但不允许被改变:
public class Lottery
{
// 创建一个列表.
List<int> _numbers = null;
public Lottery()
{
// 初始化内部列表
_numbers = new List<int>(5);
// 添加值
_numbers.Add(17);
_numbers.Add(21);
_numbers.Add(32);
_numbers.Add(44);
_numbers.Add(58);
}
public ReadOnlyCollection<int> Results
{
// 返回一份包装后的结果
get { return new ReadOnlyCollection<int>(_numbers); }
}
}
Lottery有一个内部的List<int>,它包含了在构造方法中被填的中奖号码。有趣的部分是它有一个公有属性叫Results,通过返回的ReadOnlyCollection<int>类型可以看到其中的中奖号码,从而使用户通过返回的实例来使用它。
如果用户试图设置集合中的一个值,将引发一个编译错误:
Lottery tryYourLuck = new Lottery();
// 打印结果.
for (int i = 0; i < tryYourLuck.Results.Count; i++)
{
Console.WriteLine("Lottery Number " + i + " is " + tryYourLuck.Results[i]);
}
// 改变中奖号码!
tryYourLuck.Results[0]=29;
//最后一行引发错误:// Error 26 // Property or indexer
// 'System.Collections.ObjectModel.ReadOnlyCollection<int>.this[int]'
// cannot be assigned to -- it is read only
讨论
ReadOnlyCollection的主要优势是使用上的灵活性,可以在任何支持IList或IList<T>的集合中把它做为接口使用。ReadOnlyCollection还可以象这样包装一般数组:
int [] items = new int[3];
items[0]=0;
items[1]=1;
items[2]=2;
new ReadOnlyCollection<int>(items);
这为类的只读属性的标准化提供了一种方法,并使得类库使用人员习惯于这种简单的只读属性返回类型。
阅读参考
查看MSDN文档中的“IList”和“Generic IList”主题。
4.10 使用相应的泛型版本替换Hashtable
问题
您希望通过使用相应的泛型版本替换所有Hashtable来增强应用程序性能并使得代码更为易读。当您发现这些数据结构中存放结构体和值类型会导致装箱/拆箱操作,这就变得非常有必要了。
解决方案
替换所有已存在的System.Collections.Hashtable类为速度更快的System.Collections.Generic.Dictionary泛型类。
这有一个使用System.Collections.Hashtable对象的简单例子:
public static void UseNonGenericHashtable()
{
// 创建并填充一个Hashtable.
Hashtable numbers = new Hashtable();
numbers.Add(1, "one"); // 键会导致装箱操作
numbers.Add(2, "two"); // 键会导致装箱操作
// 在Hashtable显示所有的键/值对.
// 在每次迭代中都会因为键导致一个拆箱操作
foreach (DictionaryEntry de in numbers)
{
Console.WriteLine("Key: " + de.Key + "tValue: " + de.Value);
}
numbers.Clear();
}
下面是相同的代码使用了System.Collections.Generic.Dictionary<T,U>对象:
public static void UseGenericDictionary()
{
// 创建并填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示字典中的所有键值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
numbers.Clear();
}
讨论
对于应用程序中简单的Hashtable实现,这种替换将十分容易。但有些地方需要注意,如泛型Dictionary类没有实现ICloneable接口,而Hashtable类实现了。
表4-2显示了两个类中的等价成员:
表4-2 Hashtable和泛型Dictionary类的等价成员
Hashtable 类的成员 | 泛型 Dictionary 类的相应成员 |
N/A | Comparer 属性 |
Count属性 | Count属性 |
IsFixedSize属性 | ((IDictionary)myDict).IsFixedSize |
IsReadOnly属性 | ((IDictionary)myDict).IsReadOnly |
IsSynchronized属性 | ((IDictionary)myDict).IsSynchronized |
Item属性 | Item属性 |
Keys属性 | Keys属性 |
SyncRoot属性 | ((IDictionary)myDict).SyncRoot |
Values属性 | Values属性 |
Add 方法 | Add方法 |
Clear方法 | Clear方法 |
Clone方法 | 在重载构造方法中接收一个 IDictionary<T,U> 类型 |
Contains方法 | ContainsKey方法 |
ContainsKey方法 | ContainsKey方法 |
ContainsValue方法 | ContainsValue方法 |
CopyTo方法 | ((ICollection)myDict).CopyTo(arr,0) |
Remove方法 | Remove方法 |
Synchronized static方法 | lock(myDictionary.SyncRoot) {…} |
N/A | TRyGetValue方法 |
Dictionary<int, string> numbers = new Dictionary<int, string>();
Console.WriteLine(((IDictionary)numbers).IsReadOnly);
Console.WriteLine(((IDictionary)numbers).IsFixedSize);
Console.WriteLine(((IDictionary)numbers).IsSynchronized);
Console.WriteLine(((IDictionary)numbers).SyncRoot);
注意,由于缺少返回一个泛型字典同步版本的代码,IsSynchronized属性将总是返回false。SyncRoot属性在被调用时总是返回相同的对象。实际上这个属性返回的是this指针。微软已经决定移除泛型集合类的创建同步包装的功能。
做为替代,他们推荐使用lock关键字锁住整个集合或其他同步对象类型以满足您的需求。
因为在泛型字典类中也缺少了克隆方法(实际是是因为这个类没有实现ICloneable接口),您可以转而使用重载的构造方法来接收一个IDictionary<T,U>类型:
// 创建并填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示原字典的键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Original Key: " + kvp.Key + "tValue: " + kvp.Value);
}
// 克隆字典对象.
Dictionary<int, string> clonedNumbers = new Dictionary<int, string>(numbers);
// 显示克隆字典中的键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Cloned Key: " + kvp.Key + "tValue: " + kvp.Value);
}
还有两个Dictionary类中缺少的方法:Contains和CopyTo方法。Contains方法的功能在Dictionary类中很容易被实现。在Hashtable类中,Cintains方法和ContainsKey方法有相同的行为,因此您可以在Dictionary类中简单地使用ContainsKey方法来模拟Hashtable类中的Contains方法:
// 创建和填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
Console.WriteLine("numbers.ContainsKey(1) == " + numbers.ContainsKey(1));
Console.WriteLine("numbers.ContainsKey(3) == " + numbers.ContainsKey(3));
CopyTo方法也很容易在Dictionary类中被模拟,但需要做一些额外的工作:
// 创建和填充字典.
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
// 显示字典中的所有键/值对.
foreach (KeyValuePair<int, string> kvp in numbers)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
// 创建对象数组来拷贝字典对象中的信息.
KeyValuePair<int, string>[] objs = new KeyValuePair<int,
string>[numbers.Count];
// 调用字典中的CopyTo方法
// 把字典中的所有键/值对对象拷贝到objs中
((IDictionary)numbers).CopyTo(objs, 0);
// 显示objs[]中的所有键/值对.
foreach (KeyValuePair<int, string> kvp in objs)
{
Console.WriteLine("Key: " + kvp.Key + "tValue: " + kvp.Value);
}
调用Dictionary对象中的CopyTo方法需要创建一个KeyValuePair<T,U>对象数组,它用于在CopyTo方法被调用之后,控制字典对象中的所有KeyValuePair<T,U>对象。接下来numbers字典对象被转换为IDictionary类型以调用CopyTo方法。一旦CopyTo方法被调用,objs数组将包含原numbers对象中的所有KeyValuePair<T,U>对象。注意objs数组迭代时使用了foreach循环,这点和numbers对象是相同的。
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数