泛型协方差和转换为超类型

本文关键字:超类 类型 转换 方差 泛型 | 更新日期: 2023-09-27 18:16:43

我有一个OO问题,我认为可以追溯到通用协方差。我正试图建立一个模块系统导入不同类型的记录…模块包含通用方法,而SalesModule包含处理特定逻辑的函数…

public interface IImportable { ... void BuildSqlDataRecord(); ...  }
public class Sales : IImportable { ... }
public interface IModule<out T> where T : IImportable
{
    void Import(IEnumerable<T> list);  // Error Occurs here...
    IEnumerable<T> LoadFromTextFile(TextReader sr);
}
public abstract class Module<T> : IModule<T> where T : IImportable
{ 
    public void Import(IEnumerable<T> list) { ... T.BuildSqlDataRecord(); ... } 
    public IEnumerable<T> LoadFromTextFile(TextReader sr) { ... }
}
public class SalesModule : Module<Sales>
{
    public override void BuildSqlDataRecord() { ... }; 
}

,在另一个函数中:

//Module<IImportable> module = null;
IModule<IImportable> module = null;
if(file.Name == "SALES")
    module = new SalesModule();
else
    module = new InventoryModule();
var list = module.LoadFromTextFile(sr);
module.Import(list);  

我如何声明模块以便我可以调用被覆盖的方法?

泛型协方差和转换为超类型

public interface IModule<out T> where T : IImportable
{
    void Import(IEnumerable<T> list);  // Error Occurs here...
    IEnumerable<T> LoadFromTextFile(TextReader sr);
}

错误是正确的。我们选择"out"作为协方差的关键字是为了提醒你,T只能出现在"output"的位置。在您突出显示的行中,T作为输入出现。

T不能是输入,因为…好吧,假设这是允许的,看看会发生什么糟糕的事情:

IModule<Giraffe> gm = GetMeAModuleOfGiraffes();
IModule<Animal> am = gm; // Legal because of covariance.
IEnumerable<Tiger> tigers = GetMeASequenceOfTigers();
IEnumerable<Animal> animals = tigers; // Legal because of covariance.
am.Import(animals); // Uh oh.

您刚刚将一组老虎导入到一个只知道如何处理长颈鹿的模块中。

为了防止这种情况,必须使第一步非法。带有"out"的类型声明是非法的。

我如何声明模块以便我可以调用被覆盖的方法?

必须声明接口,使其遵守协方差规则。如何做到这一点取决于你,但首先不要将任何"输出"参数放在"输入"位置。

你需要为你的模块使用一个接口:

public interface IModule<out T> where T : IImportable 
{
  void DoStuff();
  void DoOtherStuff();
}

那么你就可以像这样声明你的模块:

IModule<IImportable> = null;

查看out通用修饰符的MSDN文档