0

I was trying to write a piece of code for converting a list of strings to a list of ints I got the List<int> list = listOfStr.ConvertAll<int>(delegate(string s) { return ConvertStringToInt(s); }); line from ConvertAll .

    public static List<int> ConvertListOfStringToListOfInt(List<string> listOfStr)
    {
        List<int> list = listOfStr.ConvertAll<int>(delegate(string s) { return ConvertStringToInt(s); });

        return list;
    }
    /// <summary>
    /// converts the given str to integer
    /// </summary>
    /// <param name="str">string to be converted</param>
    /// <returns>returns the int value of the given string</returns>
    public static int ConvertStringToInt(string str)
    {
        int convertedValue = int.Parse(str);

        //(int.TryParse(str, out convertedValue))
        return convertedValue;
    }

The code is working fine except one thing.

I created Unit Test for the above method, the TestMethod is below

        /// <summary>
        ///A test for ConvertListOfStringToListOfInt
        ///</summary>
        [TestMethod()]
        public void ConvertListOfStringToListOfIntTest()
        {
            List<string> listOfStr = new List<string> { "1", "2", "3"}; // TODO: Initialize to an appropriate value
            List<int> expected = new List<int> { 1, 2, 3}; // TODO: Initialize to an appropriate value
            List<int> actual;
            actual = Conversions.ConvertListOfStringToListOfInt(listOfStr);
            Assert.AreEqual(expected, actual);
            //Assert.Inconclusive("Verify the correctness of this test method.");
        }

I have given the same values into the list I pass the method and expected list, just different by type(expected is a list of integer and passed list is a list of strings). I run the test and got this Error Message:

Assert.AreEqual failed. Expected:<System.Collections.Generic.List`1[System.Int32]>. Actual:<System.Collections.Generic.List`1[System.Int32]>.

Well, the types are actually equal so I thought there might be something different inside the lists and I debugged it.

It turned out listOfStr.Capacity is 4 and has null item as the [3] item in it's items member, for same place in items member expected has a 0 and expected.Capacity is 4,too.

However, actual.Capacity is 3 and it actually does have 3 items in it's items member.

I tried to newing actual before filling it and using list.Add() for adding values to expected and newing list in the ConvertListOfStringToListOfInt method. But the capacities are still the same. I do not want to set the capacity manually because this method will not be used for lists which has determined capaties.

What can I do to pass this test? Why is the capacities different? And how is the capacity of lists are determined, what do they depend on?

This is .NET Framework 4.0.

Community
  • 1
  • 1
Bastardo
  • 4,144
  • 9
  • 41
  • 60
  • Does the test framework have something that can compare lists (or maybe `IEnumerable`)? If so use that. If not, write one. :) – George Duckett Jun 28 '12 at 13:03
  • @GeorgeDuckett I don't really know, I create the Unit Test just by right clicking on the method and left clicking Create Unit Tests. – Bastardo Jun 28 '12 at 13:17
  • 1
    If you are using MSTest for the Unit Tests there is a CollectionAssert class that provides functionality for comparing collections which would work better for you. – Adam Gritt Jun 28 '12 at 13:22
  • @AdamGritt well, with CollectionAssert.Equals test is passed. So I guess this is the right way to compare collection, right? – Bastardo Jun 28 '12 at 13:26
  • @RoboLover - You do understand that your `ConvertStringToInt` method will cause an exception if the string cannot be parsed into an integer right? You should be using `TryParse` instead and make the method a `bool` and use the `out` argument. – Security Hound Jun 28 '12 at 13:34
  • @Ramhound I am aware of that I actually started that way as you can see the line is commented out, then I had a problem with understanding the TryParse method, that could be another question to ask. thank you for your warnin Ramhound. – Bastardo Jun 28 '12 at 13:39
  • 1
    @RoboLover - That is correct. Whenever possible use the additional Assert classes (CollectionAssert, StringAssert, etc) or Generic methods (Assert.AreEqual) to do the validation. It not only is more readable but also will perform the validation you are really looking for. – Adam Gritt Jun 28 '12 at 13:48

3 Answers3

2

The capacity of a List<T> is 4 by default. It will double every time an element is added that exceeds the current capacity if items are added one by one.

http://msdn.microsoft.com/en-us/library/y52x03h2.aspx

In my opinion, you shouldn't worry about the capacity in this case. To make your unit test pass you could change the initialization of the list:

List<int> expected = new List<int>(3) { 1, 2, 3};

Due to optimizations in the ConvertAll method will take the old capacity.

One tip:

If you are not concerned about optimizing every bit of performance, you could replace this method by

listOfStr.Select(s => int.Parse(s)).ToList();

Just to have it more maintainable.

Bas
  • 26,772
  • 8
  • 53
  • 86
  • if the default capacity is 4 how come actual's capacity is 3 Bas. Am I somehow setting it by the method ConvertListOfStringToListOfInt ? – Bastardo Jun 28 '12 at 13:17
  • Also with new List(3) { 1, 2, 3}; the capacity problem is solved but I am still getting the same Error Message. expected and actual are somehow not equal I guess. – Bastardo Jun 28 '12 at 13:22
  • You are using Array.ConvertAll, which creates the list. You are not invoking the constructor yourself. – Bas Jun 28 '12 at 13:22
  • Equality check on a list by default is ReferenceEquals -> In this case they are not the same object, so you need a different way of checking equality. http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx – Bas Jun 28 '12 at 13:23
  • Yes, with ReferenceEquals test is passed. – Bastardo Jun 28 '12 at 13:29
  • And about the optimizing every bit of performance, I am actually concerned. But thank you that looks like nice method for convertion. – Bastardo Jun 28 '12 at 13:31
2

Try this:

Assert.IsTrue(actual.SequenceEqual(expected));

The reason being is that lists are complex objects and equality for lists is not determined by each element being the same, rather it is defined as are they pointing to the same list. The sequence equal method iterates the lists for you and determines equality at the item level.

If the items of the list were complex types, then it would use the default equality comparer for those items.

John Koerner
  • 37,428
  • 8
  • 84
  • 134
  • I understand that lists are complex types but I can't get SequenceEqual after actual. , it give no extension error. – Bastardo Jun 28 '12 at 13:28
  • are you using "using System.Linq;"? – FrankE Jun 28 '12 at 13:35
  • @FrankE Yes I have that line. – Bastardo Jun 28 '12 at 13:37
  • @RoboLover: Can you give more details, which .NET Framework are you using? What is the exact error and when does it occur (compile time, runtime)? – FrankE Jun 28 '12 at 13:53
  • @FrankE I've already covered the answers for these questions in my question Frank. I am not getting any errors actually in the program it is just the test cannot be passed with the Assert method I used but I've got the solution with Adam Gritt and Bas thank you for your concern and helps. – Bastardo Jun 28 '12 at 13:58
1

Have you tried:

Assert.IsTrue(expected.SequenceEqual<int>(actual));
FrankE
  • 313
  • 1
  • 4
  • 14