我们都知道泛型在C#的重要性,泛型是OOP语言中三大特征的多态的最重要的体现,几乎泛型撑起了整个.NET框架,在讲泛型之前,我们可以抛出一个问题,我们现在需要一个可扩容的数组类,且满足所有类型,不管是值类型还是引用类型,那么在没有用泛型方法实现,如何实现?
一.泛型之前的故事我们肯定会想到用object来作为类型参数,因为在C#中,所有类型都是基于Object类型的。因此Object是所有类型的最基类,那么我们的可扩容数组类如下:
public class ArrayExpandable { private object?[] _items = null; private int _defaultCapacity = 4; private int _size; public object? this[int index] { get { if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index)); return _items[index]; } set { if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index)); _items[index] = value; } } public int Capacity { get => _items.Length; set { if (value < _size) { throw new ArgumentOutOfRangeException(nameof(value)); } if (value != _items.Length) { if (value > 0) { object[] newItems = new object[value]; if (_size > 0) { Array.Copy(_items, newItems, _size); } _items = newItems; } else { _items = new object[_defaultCapacity]; } } } } public int Count => _size; public ArrayExpandable() { _items = new object?[0]; } public ArrayExpandable(int capacity) { _items = new object?[capacity]; } public void Add(object? value) { //数组元素为0或者数组元素容量满 if (_size == _items.Length) EnsuresCapacity(_size + 1); _items[_size] = value; _size++; } private void EnsuresCapacity(int size) { if (_items.Length < size) { int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; if (newCapacity < size) newCapacity = size; Capacity = newCapacity; } }然后我们来验证下:
var arrayStr = new ArrayExpandable(); var strs = new string[] { "ryzen", "reed", "wymen" }; for (int i = 0; i < strs.Length; i++) { arrayStr.Add(strs[i]); string value = (string)arrayStr[i];//改为int value = (int)arrayStr[i] 运行时报错 Console.WriteLine(value); } Console.WriteLine($"Now {nameof(arrayStr)} Capacity:{arrayStr.Capacity}"); var array = new ArrayExpandable(); for (int i = 0; i < 5; i++) { array.Add(i); int value = (int)array[i]; Console.WriteLine(value); } Console.WriteLine($"Now {nameof(array)} Capacity:{array.Capacity}");输出:
ryzen reed wymen gavin Now arrayStr Capacity:4 0 1 2 3 4 Now array Capacity:8貌似输出结果是正确的,能够动态进行扩容,同样的支持值类型Struct的int32和引用类型的字符串,但是其实这里会发现一些问题,那就是
引用类型string进行了类型转换的验证
值类型int32进行了装箱和拆箱操作,同时进行类型转换类型的检验
发生的这一切都是在运行时的,假如类型转换错误,得在运行时才能报错
大致执行模型如下:
引用类型:
值类型:
那么有没有一种方法能够避免上面遇到的三种问题呢?在借鉴了cpp的模板和java的泛型经验,在C#2.0的时候推出了更适合.NET体系下的泛型
二.用泛型实现 public class ArrayExpandable<T> { private T[] _items; private int _defaultCapacity = 4; private int _size; public T this[int index] { get { if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index)); return _items[index]; } set { if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index)); _items[index] = value; } } public int Capacity { get => _items.Length; set { if (value < _size) { throw new ArgumentOutOfRangeException(nameof(value)); } if (value != _items.Length) { if (value > 0) { T[] newItems = new T[value]; if (_size > 0) { Array.Copy(_items, newItems, _size); } _items = newItems; } else { _items = new T[_defaultCapacity]; } } } } public int Count => _size; public ArrayExpandable() { _items = new T[0]; } public ArrayExpandable(int capacity) { _items = new T[capacity]; } public void Add(T value) { //数组元素为0或者数组元素容量满 if (_size == _items.Length) EnsuresCapacity(_size + 1); _items[_size] = value; _size++; } private void EnsuresCapacity(int size) { if (_items.Length < size) { int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; if (newCapacity < size) newCapacity = size; Capacity = newCapacity; } } }