【问题标题】:Why is this LINQ query throwing an InvalidCastException?为什么此 LINQ 查询会引发 InvalidCastException?
【发布时间】:2016-02-14 05:50:11
【问题描述】:

我在这一行收到 InvalidCastException:

List<Student> studentsWithSameSurname =
    (List<Student>)_studentsList
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .GroupBy(j => j.FamilyID);

_studentsListList&lt;Student&gt;,所以我不知道为什么 (List&lt;Student&gt;) 是无效的演员表。

对于那些想要更多上下文的人,这是一个 Winforms 应用程序,其中我将自定义类的通用列表序列化为 json 文件,然后在我想要处理数据时反序列化它,并使用 LINQ 查询它。

_studentsList 是这样定义的:

List<Student> _studentsList;

学生类是:

public class Student
{
    public int StudentID { get; set; }
    public int FamilyID { get; set; }
    public bool EnrolledInAYttFM { get; set; }
    public DateTime DateEnrolledOrHiatusAYttFM { get; set; }
    public bool GivesBibleReading { get; set; }
    public bool PresentsICRVBS { get; set; }
    public bool IsHouseholder { get; set; }
    public bool IsMale { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddr { get; set; }
    public DateTime WeekOfLastAssignment { get; set; }
    public int RecommendedNextTalkTypeID { get; set; }
    public int NextCounselPoint { get; set; }
}

我正在尝试做的事情背后的逻辑是:

如果添加了一个学生,并且他们的姓氏已经存在于通用列表中,则需要确定该姓名的第二个实例是否与前一个实例属于同一家庭的成员。更具体地说,如果添加了“Smith”,并且他们是第一个使用该姓氏的人,他们将获得下一个可用的 familyID;否则(如果该姓氏已输入),则必须确定他们是否属于已输入的一个家庭,或者他们是否是新家庭的第一个成员:

Student surNameCandidateRecord = _studentsList.SingleOrDefault(s => s.LastName == textBoxLastName.Text);
if (null == surNameCandidateRecord)
{
    student.FamilyID = _studentsList.Max(x => x.FamilyID) + 1;
}
else // at least one family with that surname exists already
{
    student.FamilyID = GetFamilyID();
}

GetFamilyID() 方法是引发此异常的方法,这里是它的全部内容:

private int GetFamilyID()
{
    List<KeyValuePair<int, string>> familyIDsAndNames;

    // InvalidCastException on the line below
    List<Student> studentsWithSameSurname = (List<Student>)_studentsList.Where(i => i.LastName == textBoxLastName.Text.Trim()).GroupBy(j => j.FamilyID);
    // </ InvalidCastException on the line above

    familyIDsAndNames = new List<KeyValuePair<int, string>>();
    foreach (Student s in studentsWithSameSurname)
    {
        string fullNameWithFamilyIdPrepended = String.Format("({0}). {1} {2}", s.FamilyID, s.FirstName, s.LastName);
        familyIDsAndNames.Add(new KeyValuePair<int, string>(s.FamilyID, fullNameWithFamilyIdPrepended));
    }
    ChooseFamilyForm cff = new ChooseFamilyForm(familyIDsAndNames);
    cff.ShowDialog();
    return AYttFMConstsAndUtils.familyIDSelected;
}

当 [n,re] InvalidCastException 被抛出时,通用列表的内容是(名称和电子邮件地址已更改以保护垃圾邮件):

[{"StudentID":1,"FamilyID":1,"EnrolledInAYttFM":true,"DateEnrolledOrHiatusAYttFM":"2016-02-12T21:52:55.9560088-08:00","GivesBibleReading":false,"PresentsICRVBS":true,"IsHouseholder":true,"IsMale":false,"FirstName":"Carly","LastName":"Shannon","EmailAddr":"carlyjeannette@att.net","WeekOfLastAssignment":"2015-08-12T21:52:50.6959113-07:00","RecommendedNextTalkTypeID":3,"NextCounselPoint":16},{"StudentID":2,"FamilyID":2,"EnrolledInAYttFM":true,"DateEnrolledOrHiatusAYttFM":"0001-01-01T00:00:00","GivesBibleReading":false,"PresentsICRVBS":true,"IsHouseholder":true,"IsMale":false,"FirstName":"Jennifer","LastName":"Volando","EmailAddr":"jenni_v@ymail.com","WeekOfLastAssignment":"0001-01-01T00:00:00","RecommendedNextTalkTypeID":0,"NextCounselPoint":0}]

...我正在尝试添加姓“Volando”的第二个人(因此将代码路由到 GetFamilyID() 方法)。

我的 LINQ 查询可能是错误的,但我不知道是哪一种方式。

更新

这是我成功使用的,基于 Tordek 的回答:

List<Student> studentsWithSameSurname = (List<Student>)_studentsList
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .OrderBy(j => j.FamilyID)
        .ToList();

我会尽快奖励 dicho answer;自从达到 10K 后,我将参加 Carnegie 并“放弃”所有分数。

【问题讨论】:

  • 因为那不是Student对象的列表...
  • 您不能将 Group By 投射到原始列表,因为 group by 返回每个家庭的学生人数。在这里,您将有两列(Count 和 FamilyId)。如果您想知道每个家庭的人数,没有理由将其投射到学生列表中。
  • 您的GroupBy 似乎没有必要;为什么要按FamilyId 对结果进行分组?

标签: c# linq generics generic-list


【解决方案1】:

在这一行

List<Student> studentsWithSameSurname =
    (List<Student>)_studentsList
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .GroupBy(j => j.FamilyID);

你没有投射_studentsList;你正在投射整个表情。换句话说,就是等价于

    (List<Student>)(_studentsList
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .GroupBy(j => j.FamilyID));

而不是(如你所料)

    ((List<Student>)_studentsList)
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .GroupBy(j => j.FamilyID);

如果您想通过 Linq 查询获取列表,则必须使用 .ToList() 方法:

     studentsList
        .Where(i => i.LastName == textBoxLastName.Text.Trim())
        .GroupBy(j => j.FamilyID)
        .ToList();

但是,您的结果不是List&lt;Student&gt;,而是一个新类型,因为GroupBy 方法创建了一个新的list-of-lists-of-students 类型。 GroupBy 类型为 IEnumerable&lt;IGrouping&lt;T, U&gt;&gt; 的表达式 os 的结果,其中 T 是您的分组变量, U 是元素的类型;在您的情况下,它将是 IEnumerable&lt;IGrouping&lt;int, Student&gt;&gt;,如果 FamilyIDint 类型。

【讨论】:

  • 那里有一个GroupBy() 呼叫......它是一组分组的集合。必须就该列表中的学生做出一些决定。
  • @JeffMercado 我在最后添加了一个说明。
  • 我会尽快奖励这个,以奖励这个答案。请查看我的更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多