使用 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");
错误并没有说它不能将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>
。
这与协方差有关。 ThermoRawFile
是MSDataFile
,但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)
);