2

我对通用抽象类及其子类有一些问题,我不知道如何解决。我有以下抽象类:

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。我愿意改变任何需要的东西,如果必须的话,甚至是我的抽象类。谢谢。我想知道使用接口而不是抽象类是否可以让我这样做。

4

5 回答 5

2

如果你把你的抽象类变成一个接口怎么办,像这样:

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 ,除非其他人有办法绕过它。

于 2012-05-11T18:21:47.647 回答
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;
    }
}

这是多态性背后的前提,因为您使用接口/基类作为子类可以/应该做什么的“定义”。这样,您将变量指定为父/基类型(在本例中为LoaderCommonPoco),并且出于功能(方法和属性)的目的,特定实现(即:CountryLoader)仅被视为父/基类型。

于 2012-05-11T16:29:43.243 回答
0

您遇到了协变和逆变类型的问题。根据您使用的 C# 版本,您可以使用“in”(协变)或“out”(逆变)标记来标记泛型类型。例如,加载程序,但在这种情况下,您的类以协变(获取安全)和逆变(设置安全)方式运行。您可以分解您的类定义以支持这两种用例,或者您可以使用更精细的方案进行类型解析。如果您使用的 IoC 容器支持继承类型的开放通用解析(Castle Windsor 支持,StructureMap 不支持),那么您可以依靠容器来确定在给定情况下要使用哪个 Loader。

于 2012-05-11T16:31:31.387 回答
0

您可以尝试将 Loader<> 中的方法抽象到标记为逆变的接口中。

public interface ILoader<out T>

然后让 Loader 基类实现这个,然后使用类似于 SPFiredrake 描述的方法,除了ILoader<CommonPoco>.

于 2012-05-11T18:18:15.960 回答
0

但是你可能会不小心调用这样的东西

villageLoader = LoadModule<Village>(**countryModuleType**) ;

当您期望一个加载器得到另一个加载器时,这将是编译器错误。请参阅@killinaswine 回答为什么 ms 开发人员认为让您这样做不是一个好主意。

您可以使用类型转换自行解决此问题。

于 2012-05-11T18:33:35.997 回答