147
public interface IDic
{
    int Id { get; set; }
    string Name { get; set; }
}
public class Client : IDic
{

}

How can I cast List<Client> to List<IDic>?

Andrew Kalashnikov
  • 3,073
  • 5
  • 34
  • 57

10 Answers10

310

You can't cast it (preserving reference identity) - that would be unsafe. For example:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Now you can convert a List<Apple> to an IEnumerable<IFruit> in .NET 4 / C# 4 due to covariance, but if you want a List<IFruit> you'd have to create a new list. For example:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

But this is not the same as casting the original list - because now there are two separate lists. This is safe, but you need to understand that changes made to one list won't be seen in the other list. (Modifications to the objects that the lists refer to will be seen, of course.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    like they're 'still in the building'! – Andras Zoltan Jan 19 '12 at 12:15
  • 1
    What is the difference between doing the covariance tolist and doing new List(); then a foreach over the original list and adding each item to the IFruit list? In the foreach way... the object reference would be the same, correct? So... if that is true, it makes little sense to me personally that you can't just directly cast the whole list. ? – Robert Noack Dec 19 '14 at 22:01
  • 3
    @RobertNoack: What do you mean by "the object reference"? The object reference of each *element* is the same, but that's not the same as casting the list reference itself. Suppose you could cast the reference, so that you had a reference of compile-time type `List` which was actually a reference to a `List`. What would you expect to happen if you added a `Banana` reference to that `List`? – Jon Skeet Dec 20 '14 at 09:50
  • 1
    Oh. I think I need to learn how to read. I thought the original answer said that the objects in the list would be deep copied and recreated which didn't make sense to me. But I clearly missed the part where only a new list is created with the same objects. – Robert Noack Dec 23 '14 at 20:06
  • Why i can't use `ToList` and `Cast` (i'n use .net4). It not show in recommend. Or i missing some namespace? – Trương Quốc Khánh Jul 29 '17 at 21:24
  • 2
    @TrươngQuốcKhánh: I have no idea what *any* of your code looks like, or whether you have a `using` directive for `System.Linq`, or what you're trying to call it on, I'm not sure how you expect me to be able to help. I suggest you do more research, and if you're still stuck, you ask a question with a [mcve]. – Jon Skeet Jul 30 '17 at 06:54
  • I can't see why you would expect/not expect a banana if you assigned an apply from your List... // Eek - it's a banana! Apple apple = apples[0]; However, it is conceivable that List could contain either an apple or a banana, so you shouldn't be able to assign a list of IFruit to a list of Apple, in the same way you can't just assign a IFruit to an Apple without casting (and risk it being a banana!)... But why not the other way round? – James Joyce Nov 24 '17 at 07:03
  • @JamesJoyce: It's reasonable to expect a `List` to only contain apples. Therefore it's surprising when it turns out it's a banana. It would basically be a violation of a sensible type system. (Bear in mind that you may not be aware of the code that's adding a banana.) – Jon Skeet Nov 24 '17 at 07:05
  • 2
    @JamesJoyce: "But why not the other way round?" Precisely because that's how you end up adding a banana - if you could perform that assignment. Which statement in `List fruit = apples; fruit.Add(new Banana());` would you expect not to work? – Jon Skeet Nov 24 '17 at 07:06
  • Aaahhh - now I understand. Its the same list!!! My bad. I was forgetting that critical point. Thanks for taking the time! – James Joyce Nov 24 '17 at 07:09
  • You could always create your own custom typecast if that's the syntax you want to use. https://stackoverflow.com/questions/1407689/how-do-i-provide-custom-cast-support-for-my-class – theB3RV Dec 13 '17 at 16:49
  • Guess down-voters were not happy that you didn't mention variance only applies to generic interface and delegates but not to generic classes:) – dragonfly02 Dec 19 '17 at 11:57
  • Coming from Java and given C# doesn't have type erasure, I would expect it to just throw an exception when trying to add a `Banana`. Basically when casting up to a `List` it would add type checks to ensure everything was the correct type. – thecodewarrior Nov 01 '19 at 15:09
  • @thecodewarrior: The point of generics is broadly to make things safe at *compile-time*. C# generics are safer in this sense than C# arrays (which *do* have reference type variance, unfortunately). – Jon Skeet Nov 01 '19 at 17:06
  • Btw, if you start off with a List of IFruit, can you add Apples and Bananas to it? How does that work? – KevinVictor Jun 19 '20 at 14:47
  • @KevinVictor: The same as if you have a variable of type `IFruit` and assign a reference to an `Apple` or `Banana` as the value. (I'm not sure what kind of answer you're looking for in terms of "How does that work".) – Jon Skeet Jun 19 '20 at 16:16
  • @JonSkeet I've noticed that `List` IS implicitly castable to `IEnumerable` - but not `IList`. Why's that? – Extragorey Aug 19 '20 at 05:21
  • @Extragorey: Because `IEnumerable` is a *covariant* interface, and `IList` isn't. It's safe to convert a `List` to `IEnumerable` because all you can do is take things out of an `IEnumerable`, and every `Apple` is an `IFruit` - but with `IList` there's a setter, so you could write `fruitList.Add(new Banana())` which clearly isn't safe. – Jon Skeet Aug 19 '20 at 06:56
  • @JonSkeet Ahh I see, so casting to `IEnumerable` is still useful for read-only usage. – Extragorey Aug 19 '20 at 23:11
  • I really just don't like the fact List can't be a param for a function asking for List... to me that seems like a language weakness. – Shiv Jul 21 '21 at 12:47
  • 1
    @Shiv: Would you prefer to wait until execution time for it to fail when that method then calls `list.Add(new Banana());`? (If the method only needs to read from the list, it should have a parameter of type `IReadOnlyList` instead, at which point it's fine.) – Jon Skeet Jul 21 '21 at 13:08
  • @JonSkeet was trying to use List readonly with the .ForEach capability as well... guess we can work around that. If readonly is required to give compile time safety I'm happy with that compromise. Makes a lot more sense. – Shiv Jul 21 '21 at 13:50
  • 1
    @Shiv: It's trivial to add your own `ForEach` extension method for `IReadOnlyList` should you wish. I'd suggest reading https://ericlippert.com/2009/05/18/foreach-vs-foreach/ before doing so though. – Jon Skeet Jul 21 '21 at 14:05
10

A Cast iterator and .ToList():

List<IDic> casted = input.Cast<IDic>().ToList() will do the trick.

Originally I said covariance would work - but as Jon has rightly pointed out; no it won't!

And originally I also stupidly left off the ToList() call

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • 1
    `Cast` returns an `IEnumerable`, not a `List` - and no, covariance *won't* allow this conversion, because it would be unsafe - see my answer. – Jon Skeet Jan 19 '12 at 11:41
  • From the page you linked to: "Only interface types and delegate types can have variant type parameters" – Jon Skeet Jan 19 '12 at 11:44
  • @Jon - I'd realised the `ToList()` was missing before reading your comment; but yes as you've shown of course Covariance won't work! Doh! – Andras Zoltan Jan 19 '12 at 11:44
  • Right. Covariance can still help, as it means you don't need the `Cast` call in .NET 4, so long as you specify the type argument to `ToList`. – Jon Skeet Jan 19 '12 at 11:45
9

I too had this problem and after reading Jon Skeet's answer I modified my code from using List<T> to use IEnumerable<T>. Although this does not answer the OP's original question of How can I cast List<Client> to List<IDic>, it does avoid the need to do so and thus may be helpful to others who encounter this issue. This of course assumes that the code that requires the use of List<IDic> is under your control.

E.g.:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

Instead of:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Ɖiamond ǤeezeƦ
  • 3,223
  • 3
  • 28
  • 40
5

If you can use LINQ then you can do this...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
musefan
  • 47,875
  • 21
  • 135
  • 185
5
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Marc Messing
  • 344
  • 1
  • 5
1

OfType

You can try something like:

        using (var dbContext = YourDatabaseContext())
        {
            var list = dbContext.Clients.Where(x => x.Happy)
                .OfType<IDic>()
                .ToList();
        }

See https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.oftype

Kwame
  • 143
  • 1
  • 6
1

If you don't need to modify the contents of the original list, you can implicitly convert a List into a IReadOnlyList which will let you iterate over it's contents as IDics without creating a new list.

List<Client> myClients = new List<Client>();
myClients.Add(new Client());

IReadOnlyList<IDic> castedClients = myClients;
foreach(IDic val in castedClients)
{
    //do something;
}

The conversion can also occur while simply returning the list like so :

public IReadOnlyList<IDic> getClientsAsIDic()
{
    return myClients;
}
Godfather
  • 1,089
  • 9
  • 21
0

In .Net 3.5, you can do the following:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

The constructor for List in this case takes an IEnumerable.
list though is only convertible to IEnumerable. Even though myObj may be convertible to ISomeInterface the type IEnumerable is not convertible to IEnumerable.

Kent Aguilar
  • 5,048
  • 1
  • 33
  • 20
  • 1
    The issue is this makes a copy of the list, most of the time you want to do operations on the original list – rollsch Jan 22 '17 at 06:33
0

If you want to process the original list without creating a separated reference, you could define the generic method like this:

public void DoIterate<T>(List<T> myCollection) where T : IDic
{
   foreach (T item in myCollection)
   {
      //update a property of interface
      item.Name = "new Name";
   }
}

Calling this method above to process the list without having to cast specific object to interface:

List<Client> clients = new List<Client>();
DoIterate(clients);
Minh Nguyen
  • 2,106
  • 1
  • 28
  • 34
0

Its only possible by creating new List<IDic> and transfering all elements.

Piotr Auguscik
  • 3,651
  • 1
  • 22
  • 30