C# 中的 Linq 查询和转换

Linq query and casting in c#

我有一个名为 BaseViewModel 的 class,并且有多个 class 继承自它。

我有一个包含所有子 class 的 List<BaseViewModel>,而不是一个 BaseViewModel

现在,我想从 List<BaseViewModel>

中提取特定类型的所有 class,比如我的 DateViewModel

现在我正在这样做,它会抛出 InvalidCastException :

CustomFieldViewModels 是我的 List<BaseViewModel>,它有一个 ControlType 枚举,我用它来识别所有 "childs".

public List<DateViewModel> DateCustomViewModels
{
    get
    {
        return (List<DateViewModel>)CustomFieldViewModels
            .Where(x => x.ControlType == CustomFieldControlValueType.Date);
    }
}

我对 Linq 相当陌生,我不确定我做错了什么。

我也很确定做一个 foreach 并填充一个 List<DateViewModel> 在性能方面不是那么好,也不是很干净。

根据我的理解,.Where 将 return 一个 List 我问的任何内容,带有过滤器(这里是我的枚举)。我不明白为什么转换会出现问题,因为我的所有子项都继承自父项 class,而且我没有在筛选中使用特定于子项的元素。另外,即使主列表的类型是 Base,none 它的元素是 Base 类型,所以首先不应该做任何转换。

我觉得我遗漏了一些非常明显的东西,但我看不到,欢迎任何帮助。

如果您提供了答案,如果您能给出一个简短的解释,而不仅仅是复制粘贴代码,我们将不胜感激:)

编辑:为了展示我最终做了什么,我选择了各种答案的组合,因为我不需要列表,但我仍然需要类型。

public IEnumerable<DateControlViewModel> DateCustomViewModels
{
    get
    {
        return CustomFieldControlViewModels.OfType<DateControlViewModel>();
    }
}

你需要使用OfType():

public List<DateViewModel> DateCustomViewModels
{
    get
    {
        return CustomFieldViewModels.OfType<DateViewModel>().ToList()
    }
}

From what I understand, the .Where will return a List of whatever I asked, with a filter (here, my enum)

不,它会 return IEnumerable<BaseViewModel>。您在 Where() 中指定的条件不会更改 return 类型,它仅指定将包含哪些 BaseViewModel 对象。

I don't understand why there is trouble casting since all my childs inherit from the parent class, and I'm not using a child-specific element in my filtering.

即使 DateViewModel 继承自 BaseViewModel,您也不能显式地从 List<DateViewModel> 转换为 List<BaseViewModel>,因为 List<T>invariant

Also, even if the type of the main list is of Base, none of its elements are of the Base type, so there shouldn't be any casting to do in the first place.

你说得对,不需要转换。使用 OfType<DateViewModel>() 只会 return DateViewModel 的对象。此外,returned 集现在是 IEnumerable<DateViewModel>(不再是 List<BaseViewModel>)并且编译器可以验证它是否与 [=24= 的 returned 类型兼容] 属性.

MSDN

从其他答案可以清楚地看出,现在无法将查询结果转换为 List<DateViewModel>。可能不清楚的是,解决方案涉及创建一个新列表。拥有一个 属性 类型的列表,可以随时调用创建一个新列表是一个 糟糕的 想法。考虑以下情况

// user of your class
yourClass.DateCustomViewModels.Add(new DateViewModel()); // goes nowhere
yourClass.DateCustomViewModels.RemoveAt(0); // removes nothing
// or trying to be smart
for (int i = 0; i < yourClass.DateCustomViewModels.Cout; i++
{
    var model = yourClass.DateCustomViewModels[i];
}
// etc.

你真正应该做的是更改你的 属性 签名

public IEnumerable<DateViewModel> DateCustomViewModels
{
    get
    {
        // use some of the suggestions in other answers
        // with ToList call removed
    }
}