使用 LINQ IEnumerable.ToDictionary() 时无法将类隐式转换为基类

本文关键字:转换 基类 IEnumerable LINQ ToDictionary 使用 | 更新日期: 2023-09-27 18:30:24

我有以下情况让我感到困惑。

鉴于:

public class ThermoRawFile : MsDataFile { }
public abstract class MSDataFile { }

我无法运行这个 LINQ IEnumerable.ToDictionary() 方法:

IEnumerable<string> rawFiles;
Dictionary<string, MSDataFile> dataFiles = rawFiles.ToDictionary(
    file => file,
    file => new ThermoRawFile(file)
);

因为编译器给出以下错误:

"Cannot implicitly convert type System.Collections.Generic.Dictionary<string,CSMSL.IO.Thermo.ThermoRawFile> to System.Collections.Generic.Dictionary<string,CSMSL.IO.MSDataFile>"

当它是简单的继承时,为什么它不能隐式地将ThermoRawFile转换为MSDataFile

这工作得很好:

 MSDataFile dataFile = new ThermoRawFile("someFile.raw");

使用 LINQ IEnumerable.ToDictionary() 时无法将类隐式转换为基类

错误并没有说它不能将CSMSL.IO.Thermo.ThermoRawFile转换为CSMSL.IO.MSDataFile,只是说它不能将基于CSMSL.IO.Thermo.ThermoRawFile的泛型字典类型转换为基于CSMSL.IO.MSDataFile的泛型词典类型。这是意料之中的,因为这些类型不是协变的。

您可以通过提供强制转换来解决此问题:

IEnumerable<string> rawFiles;
Dictionary<string, MSDataFile> dataFiles = rawFiles.ToDictionary(
    file => file,
    file => (MSDataFile)new ThermoRawFile(file)
);

问题是类型推断推断出你的ToDictionary调用是:

rawFiles.ToDictionary<string, string, ThermoRawFile>(...)

这意味着返回的值将是类型 Dictionary<string, ThermoRawFile> ,并且没有从该值到 Dictionary<string, MSDataFile> 的转换(部分原因是字典通常是不变的,部分原因是所有类都是不变的)。

现在,您可以强制转换值选择部分以使类型推断工作:

var dataFiles = rawFiles.ToDictionary(
    file => file,
    file => (MSDataFile) new ThermoRawFile());

或者,您可以显式指定类型参数:

var dataFiles = rawFiles.ToDictionary<string, string, MSDataFile>(
    file => file,
    file => new ThermoRawFile());

这仍然很好,因为编译器可以将 lambda 表达式file => new ThermoRawFile()转换为 Func<string, MSDataFile>

这与协方差有关。 ThermoRawFileMSDataFile,但Dictionary<string, ThermoRawFile>不是Dictionary<string, MSDataFile>

如果是这样,那么您可以执行以下操作:

public class FooFile : MsDataFile { }
dataFiles["blah"] = new FooFile();

这将把一个FooFile放进一个Dictionary<string, ThermoRawFile>,这将造成各种各样的破坏。

在这种情况下,您需要构造一个实际上属于 Dictionary<string, MSDataFile> 类型的字典,即使您使用完全ThermoRawFile值填充它也是如此。 您可以通过添加演员表来做到这一点(如此处的一系列其他答案所示):

IEnumerable<string> rawFiles;
Dictionary<string, MSDataFile> dataFiles = rawFiles.ToDictionary(
    file => file,
    file => (MSDataFile)new ThermoRawFile(file)
);