Easy Generics Question

J

Johnny Drama

Let's say I have an interface IScope:

internal interface IScope{}

....and I have a class that implements IScope:

internal class Scope:IScope{}

.... and I have a method that returns List<Scope>:

public List<Scope> GetScopes(){return new List<Scope>();}

.... and I'd like to pass the return value from the above method to another
method that takes a List<IScope>, say a method that looks like:

public void ProcessScopes(List<IScope> scopes){}

.... I figured that a List<Scope> could be directly converted to List<IScope>
since Scope derives from IScope, but apparently I was wrong. The conversion
doesn't work, even with an explicit cast. Could someone explain the
rationale for *why* it doesn't work? My first attempt at a workaround for
this was to build the following conversion method -- something like:

public static List<U> UpcastList<U,D>(List<D> lstD) where D:U{
List<U> lstU=new List<U>();
foreach(D d in lstD){
lstU.Add(d);
}
return lstU;
}

..this seems to work for me, but I'm still perplexed as to why the
direct conversion above fails at compile time. Any comments would be
appreciated.

Thanks..
 
N

Nicholas Paldino [.NET/C# MVP]

Drama,

Let's assume that this does work. So you have this:

List<Scope>

And in the class definition of List<T> you have something like this
(just assuming, it doesn't matter really):

List<T>
{
private T[] items;
}

So when you use List<Scope>, items is declared as type Scope[].

Now, say you have declared a class, Scope2, which implements IScope, but
does NOT derive from Scope, like so:

class Scope2 : IScope
{}

Assuming you allowed a cast from List<Scope> to List<IScope>:

// This is not legal and will not compile.
List<IScope> iScopeList = (List<IScope>) scopeList;

And then you make the call:

iScopeList.Add(new Scope2());

It would have to fail. The reason being that while you could take a
parameter of IScope, the type itself is configured to use Scope instances.
You can't just change that. Because Scope2 doesn't derive from Scope, you
can't handle the case where fields of the parameterized type are held
internally.

Hope this helps.
 
D

Dave Sexton

Hi Johnny,

Technically speaking, each List<T> declared in your code, where T differs, is created as a distinct class behind the scenes.
Although Scope implements IScope, List<Scope> doesn't derive from List<IScope>. Each are distinct classes that do not share the
same inheritance chain beyond IList and its derived interfaces. i.e., It's like casting an apple into an orange, so to speak. The
fact that the generic Types share the same inheritance chain is irrelevant.

This might be confusing since Scope[] may be passed to a method that expects IScope[], but that is because Array semantics are
handled by the runtime. The generic List class provides array semantics on a higher-level then Array and the runtime is unaware of
such an implementation and so treats it as it would any other class in the framework.
 
T

Thomas T. Veldhouse

In microsoft.public.dotnet.languages.csharp Johnny Drama said:
Let's say I have an interface IScope:

internal interface IScope{}

...and I have a class that implements IScope:

internal class Scope:IScope{}

... and I have a method that returns List<Scope>:

public List<Scope> GetScopes(){return new List<Scope>();}

List<Scope> oldScopes = new List<Scope>();
oldScopes.Add(new Scope());
List<IScope> scopes = new List<IScope>(oldScopes);

I hope that helps you out.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top