.NET基础拾遗(3)字符串、集合和流2
二、常用集合和泛型
2.1 int[]是值类型还是引用类型?
.NET中无论是存储值类型对象的数组还是存储引用类型的数组,其本身都是引用类型,其内存也都是分配在堆上的。所有的数组类型都继承自System.Array,而System.Array又实现了多个接口且直接继承自System.Object。、
不同之处则在于存储值类型对象的数组所有的值都已经包含在数组内,而存储引用类型对象的数组,其值则是一个引用,指向位于托管堆中的实例对象。
下图直观地展示了二者内存分配的差别(假设object[]中存储都是DateTime类型的对象实例):
在.NET中CLR会检测所有对数组的访问,任何视图访问数组边界以外的代码都会产生一个IndexOutOfRangeException异常。
2.2 数组之间如何进行转换?
数组类型的转换需要遵循以下两个原则:
(1)包含值类型的数组不能被隐式转换成其他任何类型;
(2)两个数组类型能够相互转换的一个前提是两者维数相同;
我们可以通过以下代码来看看数组类型转换的机制:
// 编译成功
string[] sz = { "a", "a", "a" };
object[] oz = sz;
// 编译失败,值类型的数组不能被转换
int[] sz2 = { , , };
object[] oz2 = sz;
// 编译失败,两者维数不同
string[,] sz3 = { { "a", "b" }, { "a", "c" } };
object[] oz3 = sz3;
除了类型上的转换,我们平时还可能会遇到内容转换的需求。例如,在一系列的用户界面操作之后,系统的后台可能会得到一个DateTime的数组,而现在的任务则是将它们存储到数据库中,而数据库访问层提供的接口只接受String[]参数,这时我们要做的就是把DateTime[]从内容上转换为String[]对象。当然,惯常做法是遍历整个源数组,逐一地转换每个对象并且将其放入一个目标数组类型容器中,最后再生成目标数组。But,这里我们推荐使用Array.ConvertAll方法,它提供了一个简便的转换数组间内容的接口,我们只需指定源数组的类型、对象数组的类型和具体的转换算法,该方法就能高效地完成转换工作。
下面的代码清楚地展示了普通的数组内容转换方式和使用Array.ConvertAll的数组内容转换方式的区别:
class Program
{
static void Main(string[] args)
{
String[] times ={"2008-1-1",
"2008-1-2",
"2008-1-3"}; // 使用不同的方法转换
DateTime[] result1 = OneByOne(times);
DateTime[] result2 = ConvertAll(times); // 结果是相同的
Console.WriteLine("手动逐个转换的方法:");
foreach (DateTime item in result1)
{
Console.WriteLine(item.ToString("yyyy-MM-dd"));
}
Console.WriteLine("使用Array.Convert方法:");
foreach (DateTime item2 in result2)
{
Console.WriteLine(item2.ToString("yyyy-MM-dd"));
} Console.ReadKey();
} // 逐个手动转换
private static DateTime[] OneByOne(String[] times)
{
List<DateTime> result = new List<DateTime>();
foreach (String item in times)
{
result.Add(DateTime.Parse(item));
}
return result.ToArray();
} // 使用Array.ConertAll方法
private static DateTime[] ConvertAll(String[] times)
{
return Array.ConvertAll(times,
new Converter<String, DateTime>
(DateTimeToString));
} private static DateTime DateTimeToString(String time)
{
return DateTime.Parse(time);
}
}
从上述代码可以看出,二者实现了相同的功能,但是Array.ConvertAll不需要我们手动地遍历数组,也不需要生成一个临时的容器对象,更突出的优势是它可以接受一个动态的算法作为具体的转换逻辑。当然,明眼人一看就知道,它是以一个委托的形式作为参数传入,这样的机制保证了Array.ConvertAll具有较高的灵活性。
2.3 简述泛型
泛型是.NET 2.0中推出的众多特性中最为重要的一个,方便我们设计更加通用的类型,也避免了容器操作中的装箱和拆箱操作带来的性能损失。
假如要实现一个排序算法,要能够针对各种类型进行排序。按以前的做法需要对int、double、float等类型都实现一次,但是发现除了数据类型,其他的处理逻辑完全一致。这时便可以考虑使用泛型来进行实现:
public static class SortHelper<T> where T : IComparable
{
public static void BubbleSort(T[] array)
{
int length = array.Length;
for (int i = ; i <= length - ; i++)
{
for (int j = length - ; j >= ; j--)
{
// 对两个元素进行交换
if (array[j].CompareTo(array[j - ]) < )
{
T temp = array[j];
array[j] = array[j - ];
array[j - ] = temp;
}
}
}
}
}
Microsoft在产品文档中建议所有的泛型参数名称都以T开头,作为编码的通用规范,建议大家都能遵守这样的规范。
通常泛型类被称为开放式类型,.NET中规定开放式类型不能实例化,也就确保了开放式类型的泛型参数在被指定前,不会被实例化成任何对象。为开放式的类型提供泛型的实例导致了一个封闭类型的生成,但这并不代表新的封闭类型和开放类型有任何继承关系,它们在类结构图上是处于同一层次且两者之间没有任何关系。
此外,在.NET中的System.Collections.Generic命名空间下提供了诸如List<T>、Dictionary<T>、LinkedList<T>等泛型数据结构,并且在System.Array中定义了一些静态的泛型方法,我们应该在编码实践时充分使用这些泛型容器,以提高我们的开发和系统的运行效率。
2.4 泛型的主要约束和次要约束是什么?
一个泛型参数没有任何约束时可以进行的操作和运算是非常有限的,因为不能对实参进行任何类型上的保证。
C#编译器在编译的过程中可以根据约束来检查所有泛型类型的实参并确保其满足约束条件。
(1)主要约束
一个泛型参数至多拥有一个主要约束,主要约束可以是一个引用类型、class或者struct。如果指定一个引用类型(class),那么实参必须是该类型或者该类型的派生类型。相反,struct则规定了实参必须是一个值类型。下面的代码展示了泛型参数主要约束:
public class ClassT1<T> where T : Exception
{
private T myException;
public ClassT1(T t)
{
myException = t;
}
public override string ToString()
{
// 主要约束保证了myException拥有source成员
return myException.Source;
}
} public class ClassT2<T> where T : class
{
private T myT;
public void Clear()
{
// T是引用类型,可以置null
myT = null;
}
} public class ClassT3<T> where T : struct
{
private T myT;
public override string ToString()
{
// T是值类型,不会发生NullReferenceException异常
return myT.ToString();
}
}
泛型参数有了主要约束后,也就能够在类型中对其进行一定的操作了。
(2)次要约束
次要约束主要是指实参实现的接口的限定。对于一个泛型,可以有0到无限的次要约束,次要约束规定了实参必须实现所有的次要约束中规定的接口。次要约束与主要约束的语法基本一致,区别仅在于提供的不是一个引用类型而是一个或多个接口。例如我们为上面代码中的ClassT3增加一个次要约束:
public class ClassT3<T> where T : struct, IComparable
{
......
}
作一点补充:
事实上,泛型对性能的提升仅仅对值类型有效,对引用类型几乎可以忽略不计,因为引用类型不需要装箱拆箱(不严谨)。 引用类型的泛型可以共用代码,而对于值类型来说,JIT会为每种值类型生成各自的封闭类型,并不会节省代码量,事实上,还会加重JIT编译的负担——尽管几乎可以忽略不计。 个人认为,泛型真正的优势在于编译时类型安全,结合IDE的智能提示,极大地提高了编码效率,至于性能,试想下,我们有多少情况会用到纯粹值类型的泛型呢?大部分情况下都还是引用类型的泛型。 甚至在某些极端情况下,泛型有略微降低性能的情况——对于引用类型来说。但这些依然不能妨碍其成为.NET平台最有意义的一次升级之一。
.NET基础拾遗(3)字符串、集合和流2的更多相关文章
- .NET基础拾遗(3)字符串、集合和流
Index: (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基础 ...
- .NET基础拾遗(3)字符串、集合和流3
三.流和序列化 3.1 流概念及.NET中常见流 无论什么信息,文字,声音,图像,只要进入了计算机就都被转化为数字,以数字方式运算.存储.由于计算机中使用二进制运算,因此数字只有两个:0 与 1,就是 ...
- .NET基础拾遗(3)字符串、集合和流1
一.字符串处理 1.1 StringBuilder类型 众所周知,在.NET中String是引用类型,具有不可变性,当一个String对象被修改.插入.连接.截断时,新的String对象就将被分配,这 ...
- java基础33 Set集合下的HashSet集合和TreeSet集合
单例集合体系: ---------| collection 单例集合的根接口--------------| List 如果实现了list接口的集合类,具备的特点:有序,可重复 注:集合 ...
- 基础拾遗----RabbitMQ(含封装类库源码)
基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...
- 基础拾遗----RabbitMQ
基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...
- Java基础拾遗(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76358523冷血之心的博客) 马上就要秋招了,新的一轮笔试面试马上 ...
- Java基础拾遗(一)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76358391冷血之心的博客) 马上就要秋招了,新的一轮笔试面试马上 ...
- Python基础(四) 基础拾遗、数据类型进阶
一.基础拾遗 (一).变量作用域 外层变量,可以被内层变量直接调用:内层变量,无法被外层变量使用.这种说法在其它语言中适用,在python中除了栈以外,正常的变量作用域,只要执行声明并在内存中存在,该 ...
随机推荐
- 第一个Delphi小程序
第一次应工作需呀,接触这个语言,今晚在自己的电脑搭建好环境,写的第一个超简单的Delphi小程序! var temp:Integer; //求个位数 procedure TForm1.BitBtn1C ...
- 127.0.0.1与localhost与ip的区别
127.0.0.1与localhost与ip的区别 May 18, 2014 localhost 不联网不使用网卡,不受防火墙和网卡限制本机访问 一般使用 本地套接字文件AF_UNIX 应用程序一般约 ...
- 面试题,自己写写dome总是好的
1.完成如下图所示的布局 <html> <head> <meta http-equiv="Content-Type" content="te ...
- Lua 学习笔记(一)
Lua学习笔记 1.lua的优势 a.可扩张性 b.简单 c.高效率 d.和平台无关 2.注释 a.单行注释 -- b.多行注释 --[[ --]] 3.类型和 ...
- C 产生随机码
#include<stdio.h>#include<malloc.h>#include<conio.h>#include<stdlib.h>#inclu ...
- 使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。
分布式微服务现在成为了很多公司架构首先项,据我了解,很多java公司架构都是 Maven+Dubbo+Zookeeper基础上扩展的. Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按 ...
- Android学习笔记--AlertDialog应用
1. 自定义实现带图标的TextView IconTextView.java package com.evor.andtest; import android.content.Context; imp ...
- gitosis使用笔记
gitosis是Git下的权限管理工具,通过一个特殊的仓库(gitosis-admin.git)对Git权限进行管理. 1:服务端安装并配置gitosis (1)通过以下方式获取到安装包 root@w ...
- Verdigris: Qt without moc
https://woboq.com/blog/verdigris-qt-without-moc.html https://github.com/woboq/verdigris
- Android之ExpandableListView的属性(Group不展开)
1. 设置ExpandableListView 默认是展开的: 先实例化exListView 然后 exListView.setAdapter(exlvAdapter); //遍历所有group,将 ...