C# - Yield gives an unusable type
I have a class and a set of IEnumerables that are using this class to give me a list in a list. (See this question's answer for details.)
Here is the code:
public IEnumerable<IEnumerable<WorkItemColumn>> EnumerateResultSet(WorkItemCollection queryResults)//DisplayFieldList displayFieldList)
{
foreach (WorkItem workItem in queryResults)
{
yield return EnumerateColumns(queryResults.DisplayFields, workItem);
}
}
public IEnumerable<WorkItemColumn> EnumerateColumns(DisplayFieldList resultSet, WorkItem workItem)
{
foreach (FieldDefinition column in resultSet)
yield return new WorkItemColumn { Name = column.Name, Value = workItem[column.Name], WorkItemForColumn = workItem};
}
And the class:
public class WorkItemColumn
{
public string Name { get; set; }
public object Value { get; set; }
public WorkItem WorkItemForColumn { get; set; }
}
I set this result to be the ItemsSource for my ListBox
Form.QueryResultListSource = EnumerateResultSet(queryResults);
The problem hits when I try to catch an event for this list box:
public void QueryResultsSelectionChanged(SelectionChangedEventArgs e)
{
+-----------------+
v |
foreach (WorkItemColumn workItemColumn in e.AddedItems) |
{ |
AddWorkItemToPad(workItemColumn.WorkItemForColumn); |
} +---------------|
| |
v |
foreach (WorkItemColumn workItemColumn in e.RemovedItems) |
{ |
RemoveWorkItemFromPad(workItemColumn.WorkItemForColumn); |
} |
|
} |
|
These items are where the problem is --------------------------------+
When I examine e.AddedItems[0] while debugging and it says its type is EnumerateColumns.
When I try cast to that type Visual Studio says (understandably) that EnumerateColumns is a method but is used like a type.
So, how can I reference this by type so I can do a foreach loop and get at the stuff inside it?
T开发者_StackOverflow社区his was my updated code based on the answer:
public void QueryResultsSelectionChanged(SelectionChangedEventArgs e)
{
foreach (IEnumerable<WorkItemColumn> workItemColumns in e.AddedItems)
{
if (workItemColumns.Count() > 0)
AddWorkItemToPad(workItemColumns.First().WorkItemForColumn);
}
foreach (IEnumerable<WorkItemColumn> workItemColumns in e.RemovedItems)
{
if (workItemColumns.Count() > 0)
RemoveWorkItemFromPad(workItemColumns.First().WorkItemForColumn);
}
}
The problem is that your first method isn't yielding each result given by the first - it's yielding the enumerable itself. The type of that a compiler-generated type for the iterator - but importantly, it implements IEnumerable<WorkItemColumn>
.
You can cast each item to IEnumerable<WorkItemColumn>
if you want - but it's not really clear what you're trying to do in QueryResultsSelectionChanged
. You may want to do:
foreach (IEnumerable<WorkItemColumn> workItemColumns in e.AddedItems)
{
foreach(WorkItemColumn workItemColumn in workItemColumns)
{
AddWorkItemToPad(workItemColumn.WorkItemForColumn);
}
}
etc... or you may not. It's tricky to say without knowing what you're really trying to do. Anyway, the crux of it is that you're currently trying to treat a sequence of items as a single item. Don't do that :)
e.AddedItems[0]
is an instance of a hidden iterator type generated by the compiler, which you cannot use directly.
However, it implements IEnumerable<WorkItemColumn>
, so you can use it through that interface.
In other words:
foreach (IEnumerable<WorkItemColumn> colSet in e.RemovedItems)
foreah(WorkItemColumn workItemColumn in colSet)
RemoveWorkItemFromPad(workItemColumn.WorkItemForColumn);
Although using an iterator is probably not the best approach for this problem, you can probably avoid the problem you're experiencing by storing the enumeration in an array like so:
using System.Linq;
...
Form.QueryResultListSource = EnumerateResultSet(queryResults).ToArray();
And change your EnumerateResultSet to look like:
public IEnumerable<WorkItemColumn[]> EnumerateResultSet(WorkItemCollection queryResults)//DisplayFieldList displayFieldList)
{
foreach (WorkItem workItem in queryResults)
{
yield return EnumerateColumns(queryResults.DisplayFields, workItem).ToArray();
}
}
As others have noted, using the yield keyword you are effectively returning a function that knows how to iterate over the items you wish to enumerate. But setting a data source to this enumerable does not simply execute it once and store the results. You'll likely have lots of performance problems by using iterators because anytime the results need to be used, the iteration needs to be performed all over again.
In the interest of completeness, you really should include the relevant code for AddedItems
and RemovedItems
.
Nonetheless, cast the object with the type name in fully-qualified form (meaning namespace included). This will make it bind to the type rather than the method signature, since the method won't be available at the type level but the subtype will.
精彩评论