문제
좋아, 나의 실제 문제는 이것입니다 : 나는 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
키워드는 설정된 변수를 확인할 때 항상 False를 반환합니다. null
. 이제 이것은 널리 복제 등을 피하는 등 모든 종류의 이유로 편리합니다. 내 유형 확인을 위해 마침내 내가 생각해 낸 것은 다음과 같습니다.
try
{
T test = (T)array.GetValue(0);
}
catch (InvalidCastException ex)
{
throw new ArgumentException("Cannot cast to this type of Array.", ex);
}
이것은 정확히 우아하지는 않지만 작동합니다 ... 더 좋은 방법이 있습니까?
해결책
확실히 확실한 방법은 반사를 사용하는 것이지만 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))
뿐. 너 ~해야 한다 또한 허용 typeof(T).IsAssignableFrom(elementType)
, BCL이하는 것처럼, 개발자 가이 특정의 모든 값을 알고있는 경우 ICollection
실제로 유형입니다 S
로부터 나오다 T
, 유형의 배열을 통과합니다 S[]
다른 팁
이를 위해 특별히 유형에 대한 방법이 있습니다.
if(!typeof(T).IsAssignableFrom(array.GetElementType()))
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
결론, 단지 반사 검사를하십시오. 그것은 가치.