I'm attempting to write a simple Select
method on a class that inherits from IList
.
public class RowDataCollection : IList<RowData> {
private List<RowData> rowList;
internal RowDataCollection(List<RowData> data) {
rowList = data;
}
// ...
}
public RowDataCollection Rows;
public RowDataCollection Select(string colName, object value) {
List<RowData> rowList = from item in Rows
where item[colName].Value == value
select item;
return new RowDataCollection(rowList);
}
Some problems I'm having:
First:
Cannot implicitly convert type 'IEnumerable<RowData>' to 'List<RowData>'. An explicit conversion exists (are you missing a cast?)
Cannot implicitly convert type 'IEnumerable<RowData>' to 'List<RowData>'. An explicit conversion exists (are you missing a cast?)
OK, where does the CAST go?
Second:
colName
value (ie String.IsNullOrEmpty(colName)
) or a null parameter (object value == null)
.How would I handle the way my function returns if the input parameters are invalid?
[Solved]
I edited my Select
statement (even renamed it per the suggestions here). I had to use a switch to cast to the data type that the data was in, but it does work.
public RowDataCollection SelectRow(string colName, object value) {
if (!String.IsNullOrEmpty(colName) && (value != null) && (0 < Rows.Count)) {
switch (Rows[0][colName].GetValueType()) {
case TableDataType.Boolean:
return new RowDataCollection(Rows.Where(r => (bool)r[colName].Value == (bool)value).ToList());
case TableDataType.Character:
return new RowDataCollection(Rows.Where(r => (char)r[colName].Value == (char)value).ToList());
case TableDataType.DateTime:
return new RowDataCollection(Rows.Where(r => (DateTime)r[colName].Value == (DateTime)value).ToList());
case TableDataType.Decimal:
return new RowDataCollection(Rows.Where(r => (Decimal)r[colName].Value == (Decimal)value).ToList());
case TableDataType.Integer:
return new RowDataCollection(Rows.Where(r => (int)r[colName].Value == (int)value).ToList());
case TableDataType.String:
return new RowDataCollection(Rows.Where(r => r[colName].Value.ToString() == value.ToString()).ToList());
}
}
return null;
}
[Solved (short version)]
Jon Skeet posted this about the same time I posted my solution, and (as always) his code is much nicer.
public RowDataCollection SelectRow(string colName, object value) {
List<RowData> rowList = Rows.Where(r => r[colName].Value.Equals(value)).ToList();
return new RowDataCollection(rowList);
}
@Jon Skeet: If I ever see your face in the same line at some software developer position I'm applying for, I'm just going to turn around and go home.
@Everyone : Thanks for all the help!
The result of a query like that isn't a List<T>
, it's an IEnumerable<T>
. If you want to convert that into a List<T>
, just call ToList
:
List<RowData> rowList = (from item in Rows
where item[colName].Value == value
select item).ToList();
As it happens, you're only calling Where
in your query. I would rewrite this as:
List<RowData> rowList = Rows.Where(item => item[colName].Value.Equals(value))
.ToList();
I'd also rename the method to something which is obviously filtering rather than projecting , given that the latter is the more common use of the term "select" in LINQ.
As for input parameters - I suggest you validate the arguments and throw an exception if they're not valid:
if (string.IsNullOrEmpty(colName))
{
throw new ArgumentException("colName");
}
You can't directly cast an IEnumerable<RowData>
to a List<RowData>
, however, there does exist a convenience function Enumerable.ToList<T>()
, used like so:
List<RowData> rowList = (from item in Rows
where item[colName].Value == value
select item).ToList();
As for your second question, an exception would occur during the ToList()
call as the LINQ expression is evaluated immediately. You have a few options, including throwing ArgumentException
s or returning an empty list. It depends on your use cases. I'd suggest simply throwing an exception (assuming you have some HasColumn()
method on your RowData
class):
if (colName == null)
{
throw new ArgumentNullException("colName");
}
else if (!Rows.All(row => row.HasColumn(colName)))
{
throw new ArgumentException("No such column " + colName, "colName");
}
Per your edit, another approach, if a column missing is not necessarily a "problem":
...
// note the change to Any()
else if (!Rows.Any(row => row.HasColumn(colName))
{
throw new ArgumentException("No such column " + colName, "colName");
}
List<RowData> rowList = (from item in Rows
where item.HasColumn(colName)
&& item[colName].Value == value
select item).ToList();
You're getting the error message because LINQ Queries return IEnumerable, not List.
If you need a List, it's easy enough:
List<RowData> rowList = (from item in Rows
where item[colName].Value == value
select item).ToList();
You have to convert IQueriable<> to List<>, by calling ToList();
public RowDataCollection Select(string colName, object value) {
List<RowData> rowList = from item in Rows
where item[colName].Value == value
select item;
return new RowDataCollection(rowList.ToList());
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.