在C#中创建虚拟通用方法

时间:2014-01-20 14:48:48

标签: c# generics methods virtual

我有一些像这样的基类:

public class AbstractData
{
    public int ID { get; set; }
}

public class Person: AbstractData
{
    public string Name { get; set; }
}

public class AbstractManager<T> where T: AbstractData
{
    public virtual List<T> GetAll()
    {
    }

    public virtual T GetOne(int id)
    {
    }
}

public class PersonManager: AbstractManager<Person>
{
    public override List<Person> GetAll()
    {
        //...
    }

    public override Person GetOne(int id)
    {
        //...
    }
}

现在,我有一个Windows窗体基类,如下所示:

public class BaseForm: Form
{
    public virtual AbstractManager<T> GetManager<T>() where T: AbstractData
    {
        return null;
    }
}

和派生形式:

public class PersonForm: BaseForm
{
    public override AbstractManager<T> GetManager<T>()
    {
        return new PersonManager();
    }
}

问题是,我一直在PersonForm类上遇到编译错误:

  

无法将类型'PersonManager'隐式转换为'AbstractManager&lt; T&gt;'

有没有办法可以创建这个虚方法并让从BaseForm派生的每个类都返回AbstractManager的具体表示?

如果我删除了AbstractManager类上的泛型,那么我编译好(通过一些代码更改),但GetAll方法不能返回List<T>。它必须返回List<AbstractData>,这会导致从List<Person>转换为List<AbstractData>时出现问题。

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:16)

首先,请不要这样做:

class C<T>
{
    void M<T>(T t) { }
}

现在我们在范围内有两个名为T的东西,它们是不同的。这是合法的,但非常令人困惑。为类型参数选择更好的名称。

让我们简化你的例子:

class FruitBasket<T> where T : Fruit { }
class AppleBasket : FruitBasket<Apple> { }
class C
{
    public static FruitBasket<T> GetBasket<T>() where T: Fruit 
    {
        return new AppleBasket();
    }
}

现在你明白为什么这是错的吗?如果有人打电话给C.GetBasket<Orange>()并递给他们一篮子苹果怎么办?

  

任何帮助都将不胜感激。

离开洞的步骤是什么? 停止DIGGING

你有通用性幸福病,这是C#程序员常见的,他们正在发现泛型系统的强大功能,然后想要将它用于所有事情,无论这是否有意义。停止尝试捕获泛型类型系统中业务流程中的所有关系;这不是它的设计目标。

测试是:你能说“苹果篮子是一篮子苹果,苹果是一种水果”,并且有一个不是程序员的人同意你的意见吗?是。你能否说“一个人经理是人是一种抽象数据的人的抽象经理”,并且有一个不是程序员的人同意你的看法?不。然后您没有在类型系统中成功建模业务领域。重新开始,避免使用泛型,并尝试在有意义的类型之间建立关系。

答案 1 :(得分:11)

宣布

public virtual AbstractManager<T> GetManager<T>() where T: AbstractData

BaseForm中,您承诺派生自BaseForm的每个类都支持{{1>} 任何类型{{1 }}。例如,如果您有另一个名为GetManager的{​​{1}}子类,那么您可以编写

T
预计

AbstractData将返回Invoice

如果您希望从personForm.GetManager<Invoice>() 派生的每个类仅支持{{1>} 一个类型PersonForm,请移动InvoiceManager类型参数BaseFormGetManager

T

更新: Chad Henderson指出Windows窗体设计器无法处理通用基类。如果这对您来说是个问题,那么您可以尝试另一种方法:

T