Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# .NET passing a collection of InterfaceImplementingClass objects to a routine that takes a collection of Interface objects

I have a routine

public void SomeRoutine(List<IFormattable> list) { ... }

I then try to call this routine

List<Guid>list = new List<Guid>();
list.Add(Guid.NewGuid());
SomeRoutine(list);

And it fails with a compile-time error. System.Guid implements IFormattable, but the error I get is

cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

NOTE: You will get the same error if you just use an array of Guids. Generics is NOT the cause....

But! Given this

public void SomeRoutine2(IFormattable obj) { ... }

and this

Guid a = Guid.NewGuid();
SomeRoutine2(a);

It compiles! So the question is WHY? Why am I able to pass a Guid object (which implements IFormattable) into a routine that accepts an object of IFormattable, but when I try to expand that to a collection (a generic list, or an array, or anything else), I get a conversion error?

I have had a heck of a time finding an answer, and I figured this would be the best place to go.

like image 480
emkayultra Avatar asked Feb 25 '09 00:02

emkayultra


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What does %c mean in C?

%d is used to print decimal(integer) number ,while %c is used to print character . If you try to print a character with %d format the computer will print the ASCII code of the character.

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


3 Answers

This is that whole covariance thing everyone talks about. It will work in .NET 4.0.

See here: http://blogs.msdn.com/charlie/archive/2008/10/28/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

Some more reading:

http://www.infoq.com/news/2008/08/GenericVariance;jsessionid=188695B18864997E8D360E0EEED5983E

http://blogs.msdn.com/lucian/archive/2008/10/02/co-and-contra-variance-how-do-i-convert-a-list-of-apple-into-a-list-of-fruit.aspx

like image 66
BFree Avatar answered Sep 27 '22 17:09

BFree


This is a classic generics use-case; try:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable
{ ... }

Now inside SomeRoutine you have access to all the IFormattable members, but it will work with:

List<Guid>list; ...
SomeRoutine(list); // note no need to specify T

Edit: I've additionally blogged on the differences between 4.0 covariance and generics for this scenario.

like image 31
Marc Gravell Avatar answered Sep 27 '22 18:09

Marc Gravell


The problem is that a List<IFormattable> is not only a collection from which you can read some IFormattables, it's also a collection to which you can add IFormattables. A List<Guid> meets the first requirement, but not the second. What if SomeRoutine was

public void SomeRoutine(List<IFormattable> list)
{
    list.Add(5);
}

That Int32 is an IFormattable, so the method should be able to add it to the List<IFormattable> that it asked for. That's not going to work if the compiler lets you pass in your List<Guid>.

The new C# 4.0 features that BFree refers to will let you tell the compiler when these things are safe. You can say either

  • Although I asked for an IFormattable reference, I'll only be reading it. If it's really a Guid reference then I can safely treat it as a IFormattable, and I won't try to assign an Int32 to it.
  • Although I asked for an IFormattable reference, I'll only be writing to it. If it's really an Object reference then I can safely assign my IFormattable to it, and it doesn't matter if the current value isn't an IFormattable because I won't read it.
like image 44
stevemegson Avatar answered Sep 27 '22 17:09

stevemegson