C# 中正确的可为空类型检查
本文关键字:类型 检查 | 更新日期: 2023-09-27 17:47:21
好的,我的实际问题是:我正在实现一个IList<T>
。当我到达CopyTo(Array array, int index)
时,这是我的解决方案:
void ICollection.CopyTo(Array array, int index)
{
// Bounds checking, etc here.
if (!(array.GetValue(0) is T))
throw new ArgumentException("Cannot cast to this type of Array.");
// Handle copying here.
}
这在我的原始代码中有效,并且仍然有效。但它有一个小缺陷,直到我开始为它构建测试时才暴露出来,特别是这个:
public void CopyToObjectArray()
{
ICollection coll = (ICollection)_list;
string[] testArray = new string[6];
coll.CopyTo(testArray, 2);
}
现在,这个测试应该通过。它抛出了无法投掷的ArgumentException
。为什么? array[0] == null
.is
关键字在选中设置为 null
的变量时始终返回 false。现在,由于各种原因,这很方便,包括避免空取消引用等。我最终为我的类型检查想到的是:
try
{
T test = (T)array.GetValue(0);
}
catch (InvalidCastException ex)
{
throw new ArgumentException("Cannot cast to this type of Array.", ex);
}
这并不完全优雅,但它有效...有没有更好的方法?
Type 上有一个专门针对此的方法,请尝试:
if(!typeof(T).IsAssignableFrom(array.GetElementType()))
确定的唯一方法是使用反射,但是90%的情况下,您可以使用array is T[]
来避免成本。 大多数人都会传递一个正确类型的数组,这样就可以了。 但是,为了以防万一,您也应始终提供代码来执行反射检查。 这是我的一般样板的样子(注意:我在这里写的,从记忆中,所以这可能不会编译,但它应该给出基本的想法):
class MyCollection : ICollection<T> {
void ICollection<T>.CopyTo(T[] array, int index) {
// Bounds checking, etc here.
CopyToImpl(array, index);
}
void ICollection.CopyTo(Array array, int index) {
// Bounds checking, etc here.
if (array is T[]) { // quick, avoids reflection, but only works if array is typed as exactly T[]
CopyToImpl((T[])localArray, index);
} else {
Type elementType = array.GetType().GetElementType();
if (!elementType.IsAssignableFrom(typeof(T)) && !typeof(T).IsAssignableFrom(elementType)) {
throw new Exception();
}
CopyToImpl((object[])array, index);
}
}
private void CopyToImpl(object[] array, int index) {
// array will always have a valid type by this point, and the bounds will be checked
// Handle the copying here
}
}
编辑:好的,忘了指出一些事情。 有几个答案天真地使用了在此代码中仅读取为element.IsAssignableFrom(typeof(T))
的内容。 您还应该像 BCL 一样允许 typeof(T).IsAssignableFrom(elementType)
,以防开发人员知道此特定ICollection
中的所有值实际上都是从 T
派生的类型S
,并传递一个类型的数组S[]
List<T>
使用这个:
try
{
Array.Copy(this._items, 0, array, index, this.Count);
}
catch (ArrayTypeMismatchException)
{
//throw exception...
}
以下是尝试/捕获与反射的小测试:
object[] obj = new object[] { };
DateTime start = DateTime.Now;
for (int x = 0; x < 1000; x++)
{
try
{
throw new Exception();
}
catch (Exception ex) { }
}
DateTime end = DateTime.Now;
Console.WriteLine("Try/Catch: " + (end - start).TotalSeconds.ToString());
start = DateTime.Now;
for (int x = 0; x < 1000; x++)
{
bool assignable = typeof(int).IsAssignableFrom(obj.GetType().GetElementType());
}
end = DateTime.Now;
Console.WriteLine("IsAssignableFrom: " + (end - start).TotalSeconds.ToString());
发布模式下的结果输出为:
Try/Catch: 1.7501001
IsAssignableFrom: 0
在调试模式下:
Try/Catch: 1.8171039
IsAssignableFrom: 0.0010001
结论,只需进行反射检查。这是值得的。