-1

I have a generic ListManager class with a method returning an object at a specific index. I am checking that the index is within range to avoid exceptions. What should I do if the index is out of range? I tried to return null but that's not possible. I would like to do this without using try catch. Any suggestions?

public T GetAt<T>(int index)
{
    if (index <= list.Count() - 1)
        return list[index];
    else
        return null; //This will not compile
}
Maksim Simkin
  • 9,561
  • 4
  • 36
  • 49
  • 3
    Your type `T` may not be `nullable` .. Why don't u throw a new `ArgumentOutOfRangeException` ? It's legit to handle wrong input at the place where it's passed... – Felix D. Mar 06 '17 at 20:10
  • 3
    `if (index < list.Count())` is slightly cleaner :) – Deetz Mar 06 '17 at 20:11

3 Answers3

4

you could return default(T) it's null for ref types. But for value types you will return some default value, 0 for int for example. Perhaps it is, what you want.

Maksim Simkin
  • 9,561
  • 4
  • 36
  • 49
  • The listmanager will be used both for value and ref types. How can I handle both alternatives at the same time? – Anders Lindström Mar 06 '17 at 20:34
  • You couldn't return null for value type :) So either return default value, or just throw exception and catch it. That's your application and i don't know, what should happenm if no item with this index is there – Maksim Simkin Mar 06 '17 at 20:36
1

For the sake of returning null when the index is out of range, you could write 2 versions for value and reference types

public class ValMgr<T> where T : struct
{
    public List<T> list;
    public T? GetAt(int index, T? defv = null)
    {
        if (index < list.Count)
        {
            return list[index] as T?;
        }
        return defv;
    }
}

And

 public class RefMgr<T> where T : class
 {
     public List<T> list;
     public T GetAt(int index, T defv = null) 
     {
         // don't reinvent the wheel
         T ret =  list.ElementAtOrDefault(index) as T;
         return (ret == null) ? defv : ret;
     }    
 }

Demo usage

var m = new ValMgr<int>();
m.list = new List<int>() { 1 };
var test1 = m.GetAt(0); // 1
var test2 = m.GetAt(1); // null
var test2bis = m.GetAt(1,-1); // -1
var n = new RefMgr<string>();
n.list = new List<string>() { "1" };
var test3 = n.GetAt(0); // "1"
var test4 = n.GetAt(1); // null
var test4bis = n.GetAt(1,"--"); // --

But notice that you could always use the standard, Linq ElementAtOrDefault if you simply want to return null for reference types and the default value for value types.

0

Generally, with a generic method like this, you want the caller to know that they passed an invalid value into your method. Since the type is non-nullable, the correct thing to do is to throw an exception back to the caller. You could just leave it alone and let the ArgumentOutOfRangeException bubble back to the caller. Or, if you want to use your own custom exception type or message, you would catch the ArgumentOutOfRangeException, and instantiate your own exception, setting the Inner Exception to the ArgumentOutOfRangeException that you caught.

If you don't want to use try/catch, you can use your existing logic, but in the "else" part, throw your own exception, without an inner exception.

The only other option is to return default(t), but I would not recommend this. If you do that, then the caller of the method will not know that they tried to access an invalid value, and may go on their merry way believing that the default value that they got back (say, 0 for in int or false in the case of a bool) is the actual value stored at the requested index, which would most likely lead to bugs in their solution.

JoeMjr2
  • 3,804
  • 4
  • 34
  • 62