3

I'm trying to do cast a List to an IEnumerable, so I can verify that different lists are not null or empty:

Suppose myList is a List < T > . Then in the caller code I wanted:

       Validator.VerifyNotNullOrEmpty(myList as IEnumerable<object>,
                                     @"myList",
                                     @"ClassName.MethodName");

The valdiating code would be:

     public static void VerifyNotNullOrEmpty(IEnumerable<object> theIEnumerable,
                                        string theIEnumerableName,
                                        string theVerifyingPosition)
    {
        string errMsg = theVerifyingPosition + " " + theIEnumerableName;
        if (theIEnumerable == null)
        {
            errMsg +=  @" is null";
            Debug.Assert(false);
            throw new ApplicationException(errMsg);

        }
        else if (theIEnumerable.Count() == 0)
        {
            errMsg +=  @" is empty";
            Debug.Assert(false);
            throw new ApplicationException(errMsg);

        }
    }

However, this doens't work. It compiles, but theIEnumerable is null! Why?

casperOne
  • 73,706
  • 19
  • 184
  • 253
Avi
  • 15,696
  • 9
  • 39
  • 54
  • Do any of the answers help at all? – Dave D Dec 23 '10 at 23:31
  • @Dave - Oops. Sorry. Great answers. Accepted Heinzi's because it was more focused on why my code didn't work, but yours clearly explained to me what SHOULD work. Thanks! – Avi Jan 04 '11 at 02:00

3 Answers3

6

List implements IEnumerable so you don't need to cast them, you just need to make it so your method accepted a generic parameter, like so:

 public static void VerifyNotNullOrEmpty<T>(this IEnumerable<T> theIEnumerable,
                                    string theIEnumerableName,
                                    string theVerifyingPosition)
{
    string errMsg = theVerifyingPosition + " " + theIEnumerableName;
    if (theIEnumerable == null)
    {
        errMsg +=  @" is null";
        Debug.Assert(false);
        throw new ApplicationException(errMsg);

    }
    else if (theIEnumerable.Count() == 0)
    {
        errMsg +=  @" is empty";
        Debug.Assert(false);
        throw new ApplicationException(errMsg);

    }
}

You should just be able to call it with:

var myList = new List<string>
{
    "Test1",
    "Test2"
};

myList.VerifyNotNullOrEmpty("myList", "My position");

You could also improve the implementation slightly:

 public static void VerifyNotNullOrEmpty<T>(this IEnumerable<T> items,
                                    string name,
                                    string verifyingPosition)
{
    if (items== null)
    {
        Debug.Assert(false);
        throw new NullReferenceException(string.Format("{0} {1} is null.", verifyingPosition, name));
    }
    else if ( !items.Any() )
    {
        Debug.Assert(false);
        // you probably want to use a better (custom?) exception than this - EmptyEnumerableException or similar?
        throw new ApplicationException(string.Format("{0} {1} is empty.", verifyingPosition, name));

    }
}
Dave D
  • 8,472
  • 4
  • 33
  • 45
  • This one is probably better - it demonstrates how to write a generic extension method. Of course, the code is weird - adding text to `errMsg` is useless here, but that is besides the point. In addition, I'd note that `Count()` may iterate over the entire collection, that may not be desired. I prefer `.Any()` to check if any items are available, though that might also have side effects. – Kobi May 20 '10 at 12:22
  • I agree about errMsg, but I didn't really want to change his actual implementation when just altering the signature would do what he needs... – Dave D May 20 '10 at 12:25
  • My mistake - the error message goes to the new Exception, so it does something. Missed that bit :P. Still two different types of exception are preferred. – Kobi May 20 '10 at 12:28
5

IEnumerable<object> is not a supertype of IEnumerable<T>, so it is not a supertype of List<T> either. See question 2575363 for a brief overview of why this is the case (it's about Java, but the concepts are the same). This problem has been solved in C# 4.0, by the way, which supports covariant generics.

The reason why you didn't find this error is because you used x as T, where you should have been using a normal cast ((T)x), see question 2139798. The resulting InvalidCastException would have pointed you at your error. (In fact, if the type relationship were correct (i.e. if IEnumerable<object> were a supertype of List<T>), you wouldn't need a cast at all.)

To solve your problem, make your method generic, so that it accepts an IEnumerable<T> instead of an IEnumerable<object>, and skip the cast completely.

 public static void VerifyNotNullOrEmpty<T>(IEnumerable<T> theIEnumerable,
                                            string theIEnumerableName,
                                            string theVerifyingPosition) { ... }
Community
  • 1
  • 1
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • In this case you cannot cast `(IEnumerable)myList`. – Kobi May 20 '10 at 12:39
  • @Kobi: Yes, exactly my point: If you use `(IEnumerable)myList`, you get an `InvalidCastException` and immediately know that you are using the wrong type. If you use `myList as IEnumerable`, you are left to wonder why the result is null ("Was myList null? Or did the cast fail?"). – Heinzi May 20 '10 at 12:43
  • Ok. I misunderstood you; you seem to suggest casting may work here. – Kobi May 20 '10 at 12:53
  • @Kobi: No problem, thanks for the feedback. I've updated my answer to clarify this. – Heinzi May 20 '10 at 12:58
4

Supposing you're targeting at least framework 3.0:

Cast to a generic IEnumerable<object> using extension:

var myEnumerable = myList.Cast<object>();

EDIT: anyway I'd suggest you to change your method to get a pure IEnumerable like:

public static void VerifyNotNullOrEmpty(IEnumerable theIEnumerable,
                                        string theIEnumerableName,
                                        string theVerifyingPosition)

and inside the method check if empty using foreach or theIEnumerable.Cast<object>().Count()

In this way you don't have to cast every time to IEnumerable<object>

digEmAll
  • 56,430
  • 9
  • 115
  • 140