我在使用泛型抽象类时遇到了一些问题,而且它是子类,我不知道如何解决它。我有以下抽象类:
public abstract class Loader<T> where T: CommonPoco {
public virtual List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress) {
return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture);
}
public abstract List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format);
public abstract T UploadToDb(List<T> data);
}
这是我的CommonPoco:
public class CommonPoco {
private int line;
public int Line {
get { return line; }
set { line = value; }
}
private string error;
public string Error {
get { return error; }
set { error = value; }
}
}
现在我还有许多其他的Loader子类,如下所示:
public class CountryLoader: Loader<Country> {
public override List<Country> LoadFromFile(StreamReader input,
out List<Country> result, bool trackProgress, IFormatProvider format) {
//Method implementation
}
public override Country UploadToDb(List<Country> data) {
//Method implementation
}
我还有许多CommonPoco的子类,包括Country类。到现在为止还挺好。现在问题,我想实现一个通用的方法。该方法将基于某个参数为任务使用正确的加载器。也许是这样的:
void LoadModule<T>(Module module) where T: CommonPoco {
Loader<T> loader;
switch (module) {
case Module.Country:
loader = new CountryLoader();
break;
}
}
这不起作用,编译器抱怨说它无法从CountryLoader转换为Loader。我必须创建一个方法来加载我的每个模块,除了Loader类的初始化之外,它们是完全相同的代码。我真的讨厌重复的代码所以,我怎么能实现这个目标呢?
忘了提及,使用.NET Framework 4.0。 如果必须的话,我愿意改变所需的一切,甚至是我的抽象课。谢谢。 我想知道使用接口而不是抽象类是否允许我这样做。
答案 0 :(得分:1)
如果您将抽象类转换为接口,如下所示:
public interface ILoader<T> where T : CommonPoco
{
List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress);
List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format);
T UploadToDb(List<T> data);
}
然后更改CountryLoader实现:
public class CountryLoader : ILoader<Country>
{
public List<Country> LoadFromFile(StreamReader input, out List<Country> result, bool trackProgress)
{
return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture);
}
public List<Country> LoadFromFile(StreamReader input,
out List<Country> result, bool trackProgress, IFormatProvider format)
{
//Method implementation
result = null;
return null;
}
public Country UploadToDb(List<Country> data)
{
//Method implementation
return null;
}
}
你创建了一个这样的方法:
void LoadModule<T>(Module module) where T:CommonPoco
{
ILoader<T> loader;
switch (module)
{
case Module.Country:
loader = new CountryLoader() as ILoader<T>;
break;
}
}
这个问题是你必须多次实现你的常见LoadFromFile,除非其他人有办法解决它。
答案 1 :(得分:0)
这就是它抱怨的原因:如果你试图用其他一些不是Country
而是通过国家的POCO来做这件事怎么办?示例:LoadModule<Continent>(Module.Country)
?现在应该发生什么?正是这种模棱两可会让你陷入困境。
泛型类型的重点在于它适用于您传入的任何类型,但是您尝试在泛型变量上设置特定类型,而不管通用定义,这是不好的做法(即使限制)。保持泛型通用。也就是说,只需删除通用定义,你应该没问题。
void LoadModule(Module module) {
Loader<CommonPoco> loader;
switch (module) {
case Module.Country:
loader = (Loader<CommonPoco>)new CountryLoader();
break;
}
}
这是多态性背后的前提,因为你使用接口/基类作为子类可以/应该做什么的“定义”。这样,您将变量指定为父/基类型(在本例中为Loader
和CommonPoco
),并且特定实现(即:CountryLoader
)仅被视为父/基本类型用于功能(方法和属性)。
答案 2 :(得分:0)
你遇到了协变和逆变类型的问题。根据您使用的C#版本,您可以使用“in”(协变)或“out”(逆变)标记来标记您的泛型类型。例如,Loader,但在这种情况下,您的类在协变(安全)和逆变(设置安全)方式下运行。您可以拆分类定义以支持两种用例,也可以使用更精细的方案进行类型解析。如果您正在使用支持继承类型的开放式通用解析的IoC容器(Castle Windsor没有,结构映射没有),那么您可以依靠容器来确定在给定情况下要使用哪个Loader。
答案 3 :(得分:0)
您可以尝试抽象出Loader&lt;&gt;中的方法进入标记为逆变的界面。
public interface ILoader<out T>
然后让Loader基类实现它,然后使用类似于SPFiredrake描述的方法,除了ILoader<CommonPoco>
。
答案 4 :(得分:0)
然后你可能会意外地打电话给这样的事情
villageLoader = LoadModule<Village>(**countryModuleType**) ;
并且当你期望一个加载器得到另一个加载器时,将是编译器错误。请参阅@killinaswine回答为什么ms开发人员认为让你这样做不是一个好主意。
您可以使用类型转换自行解决此问题。