Using LINQ to Initialise a List

P

Phil Ruelle

Hi,

Concise:
I've ben trying, unsuccessfully, to use the output from a LINQ query to
populate a generic List<T> object. Can someone please confirm that this isn't
actually possible using initialisation/"inline" code and actually requires a
loop (for example using the 'foreach' keyword or the ForEach extension
method)?

Verbose:
I'd like to use the object initializer syntax to populate a parent object
and all it's children. As an example consider the Parent class which has the
public property:
IList<IChild> Children
So ideally I'd like to write something along the lines of:
IParent mother = new Parent()
{ Name = "Mummy",
Children = new List<IChild>()
{ from c in data.children
select new Child()
{ Name =
c.firstname + c.familyname }
}
}

Can something like this be done? The problem seems to revovle around the
fact that the compiler looks for 'Add' methods on the collection class and
the only one available on IList and ICollection is one that takes IChild but
the LINQ query returns an IQueryable<IChild>. I've tried adding an extension
method to List<T> that accepts IQueryable<T> but the compiler does recognise
this when compiling the LINQ expression even though it does recorgnise it in
'normal' code.
I can get the correct output this by running the query outside of the
intialiser and then iterating through the results and calling add on the
Children collection but this situation just gets worse the more levels
(GrandChildren, GreatGrandChildren) are introduced.

Thanks in advance,
Phil
 
J

Jon Skeet [C# MVP]

Concise:
I've ben trying, unsuccessfully, to use the output from a LINQ query to
populate a generic List<T> object. Can someone please confirm that this isn't
actually possible using initialisation/"inline" code and actually requires a
loop (for example using the 'foreach' keyword or the ForEach extension
method)?

Well, you have to do the loop yourself - but it doesn't have to be in
that code. You could fairly easily write:

IParent mother = new Parent()
{ Name = "Mummy",
Children = new List<IChild>().AddDerivedRange
(from c in data.children
select new Child()
{ Name = c.firstname + c.familyname }
)
};

You'd do this by adding an extension method to List<T>:

public static class ListExt
{
public static List<TBase> AddDerivedRange<TBase,TDerived>
(this List<TBase> list,
IEnumerable<TDerived> elements) where TDerived : TBase
{
foreach (TBase element in elements)
{
list.Add(element);
}
return list;
}
}

Untested, but it should be okay :)

Jon
 
N

Nicholas Paldino [.NET/C# MVP]

Or, the Cast extension method on the Enumerable class can be used:

IParent mother = new Parent()
{
Name = "Mummy",
Children = new List<IChild>(
(from c in data.children
select new Child()
{ Name = c.firstname + c.familyname }
).Cast<IChild>()
};

No need to write your own extension method or make calls to Add on
List<IChild> as the constructor takes an IEnumerable<IChild>.
 
J

Jon Skeet [C# MVP]

Or, the Cast extension method on the Enumerable class can be used:

IParent mother = new Parent()
{
Name = "Mummy",
Children = new List<IChild>(
(from c in data.children
select new Child()
{ Name = c.firstname + c.familyname }
).Cast<IChild>()
};

No need to write your own extension method or make calls to Add on
List<IChild> as the constructor takes an IEnumerable<IChild>.

Yes indeed. Oops :)

Jon
 
P

Phil Ruelle

Thank you both for your replies.

A more specific question on the same topic if I may..

Using this syntax I can create a SyndicationFeed and it's associated Items.
Unfortunately the Categories property of the SyndicationItem class is
read-only so you can only add Categories by invoking methods on the
Categories collection class.

The only way I can see to facilitate the creation of complete feeds
(including items and categories) is to:
1) sub-class SyndicationItem and include an 'UpdateableCategories' property
2) create a list of these derivd classes using the new property to set the
categories
3) use .Cast<SyndicationItem>() to allow the feed's Items collection
property to be set.

Is this the only way to do this? At this stage it looks like the API
designers of the syndication classes made a 'mistake' in making the property
read-only.

Here is some example code:

feed = new SyndicationFeed(title,
description,
new Uri(url))
{ Items = new List<MySyndicationItem>
(from item in Items
select new MySyndicationItem()
{ Title =
SyndicationContent.CreatePlaintextContent(item.Title),
Content =
SyndicationContent.CreateHtmlContent(item.Content),
PublishDate = item.PublishedOn,
Id = item.Id,
updateableCategories = new
List<SyndicationCategory>()
{ new
SyndicationCategory("one"),
new
SyndicationCategory("two")
}
}
).Cast<SyndicationItem>(),
Language = "en-US",
Generator = "WCF RSS Generator"
};
 
J

Jon Skeet [C# MVP]

Thank you both for your replies.

A more specific question on the same topic if I may..

Using this syntax I can create a SyndicationFeed and it's associated Items.
Unfortunately the Categories property of the SyndicationItem class is
read-only so you can only add Categories by invoking methods on the
Categories collection class.

The only way I can see to facilitate the creation of complete feeds
(including items and categories) is to:
1) sub-class SyndicationItem and include an 'UpdateableCategories' property
2) create a list of these derivd classes using the new property to set the
categories
3) use .Cast<SyndicationItem>() to allow the feed's Items collection
property to be set.

Is this the only way to do this? At this stage it looks like the API
designers of the syndication classes made a 'mistake' in making the property
read-only.

No need to subclass. Just write a method (anywhere, really) which
returns a SyndicationItem given the relevant bits, including an
IEnumerable<SyndicationCategory>. That method would need to do the
looping, but most of your code will still be reasonably clean.

Jon
 
J

Jon Skeet [C# MVP]

No need to subclass. Just write a method (anywhere, really) which
returns a SyndicationItem given the relevant bits, including an
IEnumerable<SyndicationCategory>. That method would need to do the
looping, but most of your code will still be reasonably clean.

Correction - no need to loop explicitly; AddRange will do the trick
with a call to Cast to get the right type.
 

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