Reputation: 13517
I have to be missing something obvious here. I don't get why this cast of the results of a linq query returns null and not the typed list I'm requesting.
IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>;
The complete code to run this is below. This is a knowledge gap I need to bridge. I have tried all kinds of permutations of casts to get it to work. I get no Exceptions, just a null. Of note is that the Linq query is selecting its results into instances of my custom "MyDataClass" which implements IMyDataInterface
class Program
{
static void Main(string[] args)
{
IMyFunctionalInterface myObject = new MyClass();
//myObject.Get() returns null for some reason...
IList<IMyDataInterface> list = myObject.Get();
Debug.Assert(list != null, "Cast List is null");
}
}
public interface IMyFunctionalInterface
{
IList<IMyDataInterface> Get();
}
public class MyClass : IMyFunctionalInterface
{
public IList<IMyDataInterface> Get()
{
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = from n in names
where n.Contains("a")
select new MyDataClass
{
Name = n.ToString()
};
//There IS data in the query result
Debug.Assert(query != null, "result is null");
//but the cast here makes it return null
IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>;
return list;
}
}
public interface IMyDataInterface
{
string Name { get; set; }
}
public class MyDataClass : IMyDataInterface
{
public string Name { get; set; }
}
Upvotes: 2
Views: 13018
Reputation: 46496
The problem here is one of covariance.
First, your example is a bit too complicated. I have removed some fluff. Also, I've added some diagnostics that illuminate the problem.
class Program
{
static void Main(string[] args)
{
var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = from n in names
select new C
{
S = n
};
//There IS data in the query result
Debug.Assert(query != null, "result is null");
//but the conversion here makes it return null
var list = query.ToList() as IList<I>;
Console.WriteLine(query.ToList().GetType());
// this assert fires.
Debug.Assert(list != null, "Cast List is null");
}
}
interface I
{
string S { get; set; }
}
class C : I
{
public string S { get; set; }
}
The output of this program is:
System.Collections.Generic.List`1[C]
Note that we're trying to cast a List<C>
to List<I>
which doesn't work in C# 3.0.
In C# 4.0 you should be able to do this, thanks to the new co- and contra-variance of type parameters on generic interfaces.
Also, your original question asked about IQueryable
but that's not relevant here: the query expression you supplied creates an IEnumerable<string>
not an IQueryable<string>
.
EDIT: I want to point out that your "cast" using the as
operator is technically not a cast, but is a "type conversion". If you had use a cast, you would have gotten an exception with useful information. If I change to:
var list = (IList<I>)query.ToList();
I get an InvalidCastException
with:
Additional information: Unable to cast object of type 'System.Collections.Generic.List1[C]' to type 'System.Collections.Generic.IList
1[I]'.
Upvotes: 13
Reputation: 56391
Try this:
var query = from n in names
where n.Contains("a")
select new MyDataClass
{
Name = n.ToString()
} as IMyDataInterface;
Your problem is in this line:
IList<IMyDataInterface> list = query.ToList() as IList<IMyDataInterface>;
This can also be written as:
List<MyDataClass> tmp = query.ToList();
IList<IMyDataInterface> list = tmp as IList<IMyDataInterface>;
Unfortunately, in C# the as
operator does not work in the way you want it to. The as
operator simply casts the list object as a list of a different type; it does not attempt to go through the list and cast every item. To cast a list to something else, you need to call the Cast extension method on it. E.g.:
IList<IMyDataInterface> list = query.ToList().Cast<IMyDataInterface>();
So, your options are: cast ever item in your query as the interface you want (my first example) or cast the entire list after you've executed your query (my second example).
I suggest the former.
Upvotes: 5