IList<IList<int>> doesn't accept List<List<int>>? - c#

List implements IList so I expect IList will accept a List object
but why IList> doesn't accept List>?
static IList<int> List_1()
{
List<int> list = new List<int> { 1,2,3,3,4,5};
return list;
}
static IList<IList<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; //compiler error CS0266
}

That's because of
List<T> implements IList<T> but
List<List<T>> does not implement IList<IList<int>>
That's why your first method works as intended and second not.
Just change your declaration of the list in the second method to
List<IList<int>> parent = new List<IList<int>>();
And this is the case of covariance and contravariance.
Generic type parameters support covariance and contravariance but you need to define in that way
By learn.microsoft.com
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types

Suppose this works. Your client code is:
var result = List_2();
Since the contract allows adding to the result anything that's IList<int>, you could possibly have
public class MyCustomIList : IList<int>
{
...
}
and then
var result = List_2();
result.Add( new MyCustomIList() );
But that's wrong!
Your result is a list of List<int>, you should not be allowed to add anything other than List<int> or its derivatives there. However, you were able to add MyCustomIList which is not related to the List<int>.
If you need a broad picture of the issue, read more on covariance and contravariance.
The fundamental issue in this particular example comes from the Add operation. If you don't need it, the IEnumerable will do
static IEnumerable<IEnumerable<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; // no error, this works
}
This has been covered already.

Why then does List implement IList?
It is a bit odd, since List for any type other than object does not
fulfill the full contract of IList. It's probably to make it easier on
people who are updating old C# 1.0 code to use generics; those people
were probably already ensuring that only the right types got into
their lists. And most of the time when you're passing an IList around,
it is so the callee can get by-index access to the list, not so that
it can add new items of arbitrary type.
I would suggeste return IEnumerable instead of IList, will simplify your life, since List Fully implements it.

The problem is with your method return type. Modify your method signature to return to IList<List<int>> rather than returning IList<IList<int>>
static IList<List<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent; //no compiler error
}
Now it will work fine as your method now returns an IList of List<int>

I don't know why you want to return exactly IList<IList<int>>, but one way of doing that is to use Cast<T>() method:
static IList<IList<int>> List_2()
{
List<List<int>> parent = new List<List<int>>();
List<int> list = new List<int> { 1, 2, 3, 3, 4, 5 };
parent.Add(list);
return parent.Cast<IList<int>>().ToList();
}
Or ConvertAll() method:
return parent.ConvertAll(x => (IList<int>)x);
Both methods will run over all elemets, and cast/convert them to a given type, so I think it would be better to return IList<List<int>> instead (if that's possible).

Related

Accept "params" which are lists themselves?

I'm trying to write a method for a project which takes any number of lists as parameters, and returns a new list containing terms which ALL of those lists share. I have functional code, but I'd much prefer to use the params keyword rather than having to create a list of lists which holds all the lists I want to compare.
static List<T> Shared<T>(List<T> first, List<T> second)
{
List<T> result = new List<T>();
foreach (T item in first)
if (second.Contains(item) && !result.Contains(item)) result.Add(item);
return result;
}
static List<T> Shared<T>(List<List<T>> lists)
{
List<T> result = lists.First();
foreach (List<T> list in lists.Skip(1))
{
result = Shared<T>(result, list);
}
return result;
}
Is my current code, which works fine comparing two lists, but in order to compare more than two lists I have to either create a new list like:
List<int> nums1 = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> nums2 = new List<int> { 1, 2, 3 };
List<int> nums3 = new List<int> { 6, 5, 3, 2 };
List<int> listOfLists = Shared<int>(new List<List<int>> {nums1, nums2, nums3});
foreach (int item in listOfLists)
Console.WriteLine(item);
//Writes 2 and 3
etc. I would really wish to just be able to use Shared(list1, list2, list3, list4...) instead, even if this code is already somewhat functional. Currently any attempts to use a params version complains that "No overload for method 'Shared' takes N arguments"
Also I know my code could probably be done more efficiently, so I'd be glad to see suggestions on that too but primarily I need to get my head around why using params isn't working - if it's even possible.
Are you looking for this?
static List<T> Shared<T>(params List<T>[] lists)
The params parameter must always have an array type, but it can be an array of Lists.
It can be done quiet easily:
using System.Linq;
// ..
static List<T> Shared<T>(params List<T>[] lists)
{
if (lists == null)
{
throw new ArgumentNullException("lists");
}
return Shared(lists.ToList());
}
Building on the response of #Selman22 who proposed the method signature, you could alternatively use this LINQ query, to achieve the desired result.
static List<T> Shared<T>(params List<T>[] lists)
{
return
lists.Skip(1).Aggregate( // Skip first array item, because we use it as a seed anyway
lists.FirstOrDefault(), // Seed the accumulator with first item in the array
(accumulator, currentItem) => accumulator.Intersect(currentItem).ToList()); // Intersect each item with the previous results
}
We skip the first item that is being used as the seed for the accumulator, and do an intersect with the accumulator for each item in the given params array, since only the items that are contained in ALL the lists are kept in the accumulator result.
To test it out, you can use
Shared(nums1, nums2, nums3).ForEach(r => Console.WriteLine(r));

List interface: from Java to C#

I'm a Java programmer learning C# these days.
Usually in Java when using lists, it should be preferrable programming against its interface in order to switch between implementations:
List<Object> list = new ArrayList<Object>();
//or
list = new LinkedList<Object>();
What about C# ? Does exist a similar approach? Can someone show me an example? Since now I'm building a list this way, but I don't think List is an interface:
List<int> list = new List<int>();
list.Add(2);
In .NET it is also preferable to work with the highest possible object in the hierarchy. You could use the IList<T> interface:
IList<int> list = new List<int>();
list.Add(2);
And if you don't need to access the list by index you could also use the ICollection<T> interface which is even higher in the hierarchy.
Or if you only want to enumerate through the list you could use the highest possible interface which is IEnumerable<T>:
IEnumerable<int> list = new List<int>(new[] { 1, 2, 3 });
foreach(int item in list)
{
...
}
IList<int> = new List<int>();
in C# it's easy - if it starts with an I its an interface.
List<T> implements a number of interfaces, including IList<T> and ICollection<T>. You may need to examine your code to determine which interface is most appropriate.
In .net IList<T> is the interface. And you can assign it any of the interface's implementations e.g., List<T>. See the implementations of this interface: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#inheritanceContinued

List<List<int>> Remove() method

I'd like to use Remove() method on list of lists, but it's not working for me.
Simple example should say everything:
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(new List<int> { 1, 2 });
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(new List<int> { 1, 2 });
If I use RemoveAt(1) it works fine but Remove() not.
It is obviously the same reason that this code returns false:
List<int> l1 = new List<int>();
List<int> l2 = new List<int>();
l1.Add(1);
l2.Add(1);
bool b1 = l1 == l2; // returns False
bool b2 = l1.Equals(l2); // returns False too
So it seems to me that I cannot simply compare two lists or even arrays. I can use loops instead of Remove(), but there must be easier way.
Thanks in advance.
The problem is that List<T> doesn't override Equals and GetHashCode, which is what List<T> will use when trying to find an item. (In fact, it will use the default equality comparer, which means it'll use the IEquatable<T> implementation if the object implements it, and fall back to object.Equals/GetHashCode if necessary). Equals will return false as you're trying to remove a different object, and the default implementation is to just compare references.
Basically you'd have write a method to compare two lists for equality, and use that to find the index of the entry you want to remove. Then you'd remove by index (using RemoveAt). EDIT: As noted, Enumerable.SequenceEqual can be used to compare lists. This isn't as efficient as it might be, due to not initially checking whether the counts are equal when they can be easily computed. Also, if you only need to compare List<int> values, you can avoid the virtual method call to an equality comparer.
Another alternative is to avoid using a List<List<int>> in the first place - use a List<SomeCustomType> where SomeCustomType includes a List<int>. You can then implement IEquatable<T> in that type. Note that this may well also allow you to encapsulate appropriate logic in the custom type too. I often find that by the type you've got "nested" collection types, a custom type encapsulates the meaning of the inner collection more effectively.
First approach:
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.Except(listToRemove).Count() == 0);
This also removes the List { 2, 1 }
Second approach (preferred):
List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.SequenceEqual(listToRemove));
This removes all lists that contain the same sequence as the provided list.
List equality is reference equality. It won't remove the list unless it has the same reference as a list in the outer list. You could create a new type that implements equality as set equality rather than reference equality (or you do care about order as well?). Then you could make lists of this type instead.
This simply won't work because you're tying to remove a brand new list (the new keyword kind of dictates such), not one of the ones you just put in there. For example, the following code create two different lists, inasmuch as they are not the same list, however much they look the same:
var list0 = new List<int> { 1, 2 };
var list1 = new List<int> { 1, 2 };
However, the following creates one single list, but two references to the same list:
var list0 = new List<int> { 1, 2 };
var list1 = list0;
Therefore, you ought to keep a reference to the lists you put in there should you want to act upon them with Remove in the future, such that:
var list0 = new List<int> { 1, 2 };
listOfLists.Remove(list0);
They are different objects. Try this:
List<int> MyList = new List<int> { 1, 2 };
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(MyList);
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });
list.Remove(MyList);
You need to specify the reference to the list you want to remove:
list.Remove(list[1]);
which, really, is the same as
list.RemoveAt(1);

Cast using Reflection in C#

I've created a generic function as below (just a s a proof) that will take a List<T> collection and reverse it, returning a new List<T> as its output.
public static List<T> ReverseList<T>(List<T> sourceList)
{
T[] outputArray = new T[sourceList.Count];
sourceList.CopyTo(outputArray);
return outputArray.Reverse().ToList();
}
The purpose of the proof is that I only know what T is at runtime. I am therefore using reflection to call the above method as follows:
List<int> myList = new List<int>() { 1, 2, 3, 4, 5 }; // As an example, but could be any type for T
MethodInfo myMethod = this.GetType().GetMethod("ReverseList");
MethodInfo resultMethod = myMethod.MakeGenericMethod(new Type[] { typeof(int) });
object result = resultMethod.Invoke(null, new object[] { myList });
There are two problems here:
In the second line, rather than supplying typeof(int), I would like suppliy somthign akin to myList.GetType().GetGenericArguments()[0].GetType() in order to make things more flexible because I do not know T until runtime. Doing this results in a runtime error when the Invoke runs as follows: "Object of type 'System.Collections.Generic.List'1[System.Int32]' cannot be converted to type 'System.Collections.Generic.List'1[System.RuntimeType]'."
The result of the Invoke() method returns an object. When debugging, I can see that the object is of type List, but attempting to use it tells me that I have an invalid cast. I assume that I need to use reflection to box the result in to the correct type (i.e. in this example, the equivalent of (result as List<int>).
Does anyone have any pointers that could help me resolve this? Apologies if this is not to clear, I can probably provide more detail if asked.
TIA
You've got one GetType() too many. Happens to everyone.
myList.GetType().GetGenericArguments()[0] IS a System.Type -- the one you're looking for.
myList.GetType().GetGenericArguments()[0].GetType() is a System.Type describing System.Type (well, actually the concrete subclass System.RuntimeType).
Also, your ReverseList function is serious overkill. It does an extra copy just to avoid calling List.Reverse. There's a better way to circumvent that:
public static List<T> ReverseList<T>(List<T> sourceList)
{
return Enumerable.Reverse(sourceList).ToList();
}
or
public static List<T> ReverseList<T>(List<T> sourceList)
{
var result = new List<T>(sourceList);
result.Reverse();
return result;
}
or
public static List<T> ReverseList<T>(List<T> sourceList)
{
var result = new List<T>();
result.Capacity = sourceList.Count;
int i = sourceList.Count;
while (i > 0)
result.Add(sourceList[--i]);
return result;
}
To access it as a List<T>, yes you'd need to find T using reflection (probably over the interfaces, for example typeof(IList<>), and use more reflection and MakeGenericMethod etc. In all honesty, it isn't worth it: you would do better to check for the non-generic IList:
var list = result as IList;
if (list != null)
{
// loop over list etc
}
Generics ad reflection are not good friends.
Note in 4.0 there are also some tricks you can do here with dynamic and generics.
The result of the Invoke() method
returns an object. When debugging, I
can see that the object is of type
List, but attempting to use it tells
me that I have an invalid cast. I
assume that I need to use reflection
to box the result in to the correct
type (i.e. in this example, the
equivalent of (result as List).
The only workaround for this is I can think of is to pass an empty list as the second parameter of the method and to populate that list - the reference returned by Invoke() will always be only of type object, but inside the generic method you do have access to the type itself:
List<int> reverseList = new List<int>();
resultMethod.Invoke(null, new object[] { myList, reverseList });
...
public static void ReverseList<T>(List<T> sourceList, List<T> resultList)
{
T[] outputArray = new T[sourceList.Count];
sourceList.CopyTo(outputArray);
resultList.AddRange(outputArray.Reverse());
}

C# Function Chaining

Why do i receive error in the following declaration ?
List<int> intrs = new List<int>().AddRange(new int[]{1,2,3,45});
Error :Can not convert type void to List ?
Because AddRange function does not return a value. You might need to perform this in two steps:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
You could also use a collection initializer (assuming C# 3.0+).
List<int> intrs = new List<int> { 1, 2, 3, 45 };
Edit by 280Z28: This works for anything with an Add method. The constructor parenthesis are optional - if you want to pass thing to a constructor such as the capacity, you can do so with List<int>(capacity) instead of just List<int> written above.
Here's an MSDN reference for details on the Object and Collection Initializers.
Dictionary<string, string> map = new Dictionary<string, string>()
{
{ "a", "first" },
{ "b", "second" }
};
Because AddRange modifies the specified list instead of returning a new list with the added items. To indicate this, it returns void.
Try this:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
If you want to create a new list without modifying the original list, you can use LINQ:
List<int> intrs = new List<int>();
List<int> newIntrs = intrs.Union(new int[]{1,2,3,45}).ToList();
// intrs is unchanged
AddRange does not return the list it has added items to (unlike StringBuilder). You need to do something like this:
List<int> intrs = new List<int>();
intrs.AddRange(new int[]{1,2,3,45});
AddRange() is declared as:
public void AddRange(object[]);
It does not return the list.
By the way in C# 3.x (not sure about 2.0) you can do either of
List<int> intrs = new List<int>{1,2,3,45};
List<int> intrs = new []{1,2,3,45}.ToList(); // with Linq extensions
Besides other answers, you can add your own extension method that will add range and return list (not that it's a good practice).
BTW, if you had already declared intrs, you could have done it with parentheses:
(intrs = new List<int>()).AddRange(new int[] { 1, 2, 3, 45 });
However, I like the initialization syntax better.
Although others have already mentioned that AddRange does not return a value, based on the samples given for alternatives it should also be remembered that the constructor of List will take an IEnumerable of T as well in addition to the code previously mentioned that is .NET 3.5+
For example:
List<int> intrs = new List<int>(new int[]{2,3,5,7});
There is yet another way.
List<int> intrs = new List<int>
{
1,
2,
3,
45
};

Categories

Resources