c#泛型抽象类和泛型方法

时间:2012-05-11 16:13:22

标签: c# .net oop

我在使用泛型抽象类时遇到了一些问题,而且它是子类,我不知道如何解决它。我有以下抽象类:

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。 如果必须的话,我愿意改变所需的一切,甚至是我的抽象课。谢谢。 我想知道使用接口而不是抽象类是否允许我这样做。

5 个答案:

答案 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;
    }
}

这是多态性背后的前提,因为你使用接口/基类作为子类可以/应该做什么的“定义”。这样,您将变量指定为父/基类型(在本例中为LoaderCommonPoco),并且特定实现(即: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开发人员认为让你这样做不是一个好主意。

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