Correct NULL type checking in C #?

Well, my actual problem was this: I implemented 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 source code and still works. But he has a small flaw that was not shown until I started building tests for him, in particular 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 an ArgumentException due to the inability to throw. What for? array[0] == null . The is keyword always returns false when checking for a variable that is set to null . Now it is convenient for various reasons, including the exclusion of zero differences, etc. What I finally came up with for type checking was the following:

 try { T test = (T)array.GetValue(0); } catch (InvalidCastException ex) { throw new ArgumentException("Cannot cast to this type of Array.", ex); } 

It's not quite elegant, but it works ... Is there a better way?

+4
source share
4 answers

The only way to be convinced of reflection, but in 90% of cases you can avoid cost of it, using array is T[] . Most people are going to pass a correctly printed array, so that will do it. But, you should always provide a code for checking reflection, just in case. This is what my common boiler stove looks like (note: I wrote it here from memory, so this may not compile, but this should give the main idea):

 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 } } 

EDIT : Alright, forgot to say something. The couple naively uses what is read only as element.IsAssignableFrom(typeof(T)) in this code. You must also enable typeof(T).IsAssignableFrom(elementType) , as BCL does, if the developer knows that all values ​​in this particular ICollection actually of type S , obtained from T , and pass an array of type S[]

+3
source

There is a special method for this type, try:

 if(!typeof(T).IsAssignableFrom(array.GetElementType())) 
+4
source

List<T> uses this:

 try { Array.Copy(this._items, 0, array, index, this.Count); } catch (ArrayTypeMismatchException) { //throw exception... } 
+1
source

Here is a small try / catch test against reflection:

 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()); 

The resulting output in Release mode:

 Try/Catch: 1.7501001 IsAssignableFrom: 0 

In debug mode:

 Try/Catch: 1.8171039 IsAssignableFrom: 0.0010001 

Conclusion, just do a reflection test. It's worth it.

0
source

Source: https://habr.com/ru/post/1277462/


All Articles