Ok, my actual problem was this: I was implementing an IList<T>. When I got to CopyTo(Array array, int index), this was my solution:
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. }
This worked in my original code, and still works. But it has a small flaw, which wasn’t exposed till I started building tests for it, specifically this one:
public void CopyToObjectArray() { ICollection coll = (ICollection)_list; string[] testArray = new string[6]; coll.CopyTo(testArray, 2); }
Now, this test should pass. It throws the ArgumentException about not being able to cast. Why? array[0] == null. The is keyword always returns false when checking a variable that is set to null. Now, this is handy for all sorts of reasons, including avoiding null dereferences, etc. What I finally came up with for my type checking was this:
try { T test = (T)array.GetValue(0); } catch (InvalidCastException ex) { throw new ArgumentException('Cannot cast to this type of Array.', ex); }
This isn’t exactly elegant, but it works… Is there a better way though?
The only way to be sure is with reflection, but 90% of the time you can avoid the cost of that by using
array is T[]. Most people are going to pass a properly typed array in, so that will do. But, you should always provide the code to do the reflection check as well, just in case. Here’s what my general boiler-plate looks like (note: I wrote this here, from memory, so this might not compile, but it should give the basic idea):EDIT: Ok, forgot to point something out. A couple answers naively used what, in this code, reads as
element.IsAssignableFrom(typeof(T))only. You should also allowtypeof(T).IsAssignableFrom(elementType), as the BCL does, in case a developer knows that all of the values in this specificICollectionare actually of a typeSderived fromT, and passes an array of typeS[]