问题:
你需要用一种类型参数来创建泛型类型,该类型参数必须支持特定接口的成员,如IDisposable。
解决方案:
使用约束条件强制要求泛型类型的类型参数是一种实现一个或多个特定接口的类型。
public class DisposableList<T> : IList<T>
where T : class, IDisposable
{
private List<T> _items = new List<T>();
// 私有方法,处置列表中的元素
private void Delete(T item) => item.Dispose();
// IList<T> Members
public int IndexOf(T item) => _items.IndexOf(item);
public void Insert(int index, T item) => _items.Insert(index, item);
public T this[int index]
{
get { return (_items[index]); }
set { _items[index] = value; }
}
public void RemoveAt(int index)
{
Delete(this[index]);
_items.RemoveAt(index);
}
// ICollection<T> Members
public void Add(T item) => _items.Add(item);
public bool Contains(T item) => _items.Contains(item);
public void CopyTo(T[] array, int arrayIndex) =>
_items.CopyTo(array, arrayIndex);
public int Count => _items.Count;
public bool IsReadOnly => false;
// IEnumerable<T> Members
public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
// IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();
// Other members
public void Clear()
{
for (int index = 0; index < _items.Count; index++)
{
Delete(_items[index]);
}
_items.Clear();
}
public bool Remove(T item)
{
int index = _items.IndexOf(item);
if (index >= 0)
{
Delete(_items[index]);
_items.RemoveAt(index);
return (true);
}
else
{
return (false);
}
}
}
这个DisposableList 类只允许用一个实现了IDisposable 接口的对象作为类型参数传入。其原因是,无论何时从DisposableList 对象中删除一个对象,都会在该对象上调用Dispose 方法。这允许你透明地管理这个DisposableList 对象中存储的任何对象。
下面的代码用到了DisposableList 对象:
public static void TestDisposableListCls()
{
DisposableList<StreamReader> dl = new DisposableList<StreamReader>();
// 创建一些测试对象
StreamReader tr1 = new StreamReader("C:\\Windows\\system.ini");
StreamReader tr2 = new StreamReader("c:\\Windows\\vmgcoinstall.log");
StreamReader tr3 = new StreamReader("c:\\Windows\\Starter.xml");
// 将测试对象添加到DisposableList
dl.Add(tr1);
dl.Insert(0, tr2);
dl.Add(tr3);
foreach (StreamReader sr in dl)
{
Console.WriteLine($"sr.ReadLine() == {sr.ReadLine()}");
}
// 在从DisposableList中移除任何可处置的对象之前调用Dispose方法
dl.RemoveAt(0);
dl.Remove(tr1);
dl.Clear();
}
讨论:
where 关键字是用于约束类型参数只接受满足给定约束条件的参数。例如,DisposableList约束任何类型参数T 必须实现 IDisposable 接口。
public class DisposableList<T> : IList<T>
where T : IDisposable
这意味着下面的代码将成功地编译:
DisposableList<StreamReader> dl = new DisposableList<StreamReader>();
但是下面的代码则不然:
DisposableList<string> dl = new DisposableList<string>();
这是由于string 类型没有实现IDisposable 接口,而StreamReader 类型则实现了这个接口。
除了要求实现一个或多个特定的接口之外,还允许对类型参数施加其他约束条件。可以强制类型参数继承自特定的基类(如TextReader 类)。
public class DisposableList<T> : IList<T>
where T : System.IO.TextReader, IDisposable
还可以确定是否把类型参数限制为只能是值类型或引用类型。下面的类声明被限制为只使用值类型。
public class DisposableList<T> : IList<T>
where T : struct
下面这个类声明被限制为只使用引用类型。
public class DisposableList<T> : IList<T>
where T : class
此外,还可以要求类型参数实现公开的默认构造函数。
public class DisposableList<T> : IList<T>
where T : IDisposable, new()
使用约束条件允许编写只接受一组小范围可用类型参数的泛型类型。如果1.12.2 节中省略了IDisposable 约束条件,将会发生编译时错误。这是由于并非所有的类型都可用作将实现IDisposable 接口的DisposableList 类的类型参数。如果跳过这个编译时检查,DisposableList 对象就可能包含没有公开的无参Dispose 方法的对象。在这种情况下,就会发生运行时异常。泛型,尤其是约束条件可以强制对类型的参数执行严格的类型检查,并且允许在编译时而非运行时捕获这些问题。
参考:
MSDN 文档中的“where 关键字”主题。