C#泛型 - 如何返回特定类型?

时间:2010-02-11 18:15:11

标签: c# generics inheritance

也许我说这一切都错了。

我有一堆派生自“Model”类的类,一个带有一堆常见属性和方法的基类。我希望他们都能实现一组功能:

public abstract void Create();
public abstract T Read<T>(Guid ID);  //<--Focus on this one
public abstract void Update();
public abstract void Delete();

然后我在像“约会”这样的子类中实现它,如下所示:

public override T Read<T>(Guid ID)
{
  var appt = db.Appointments.First(a => a.AppointmentID.Equals(ID));
  var appointment = new Appointment()
  {
    DateEnd = appt.dateEnd.GetValueOrDefault(),
    Location = appt.location,
    Summary = appt.summary
  };
return appointment;
}

这会引发异常“无法将类型'约会'隐式转换为T”。如果我将方法的签名更改为“public override Appointment Read(Guid ID)”,那么编译器会说我没有在子类中实现抽象方法。

我错过了什么?谁能给我一些代码样本?

9 个答案:

答案 0 :(得分:19)

看起来您可以使用通用基类!考虑以下内容:

class Model<T>
{
    public abstract T Read(Guid ID);
}

class Appointment : Model<Appointment>
{
    public override Appointment Read(Guid ID) { }
}

现在您的子类都是强类型的。当然,权衡是你不再拥有一个基类。 Model<Appointment>Model<Customer>不同。我一般都没有发现这是一个问题,因为它没有什么共同的功能 - 接口是相似的,但它们都使用不同的类型。

如果你想要一个共同的基础,你当然可以欺骗并实现一个基于object的界面来完成相同的一般任务。例如,本着(未经测试,但想法在那里)的精神:

interface IModelEntity
{
    object Read(Guid ID);
}

class Model<T> : IModelEntity
{
    public T Read(Guid ID)
    {
        return this.OnRead(ID); // Call the abstract read implementation
    }

    object IModelEntity.Read(Guid ID)
    {
        return this.OnRead(ID); // Call the abstract read implementation
    }

    protected abstract virtual T OnRead(Guid ID);
}

class Appointment : Model<Appointment>
{
    protected override Appointment OnRead(Guid ID) { /* Do Read Stuff */ }
}

答案 1 :(得分:5)

这会有用吗?

public abstract T Read<T>(Guid ID) where T : IAppointment;

答案 2 :(得分:4)

你需要装盒和施法。我想知道为什么这种方法是通用的?

return (T)(object)appointment;

答案 3 :(得分:3)

您必须先转为object然后转为T。原因在于object位于继承链的顶端。 AppointmentT之间没有直接关联;因此,您必须回溯到object,然后返回T

我提供了这个答案来解释为什么返回陈述不会起作用,除非它被双重演绎 - 并且支持Chaos&amp; amp;格雷格

答案 4 :(得分:2)

首先,我建议您将基类转换为界面。如果这是一个选项,这也会减少稍微混乱的代码,因为你可以摆脱接口声明中的abstractpublic关键字,并省略override in实施班。

第二次,正如Appointment.Read的实施建议,您可以更改Read的方法签名以返回模型对象。

两个建议的更改都会导致以下结果:

public interface IModel
{
    void Create();
    IModel Read(Guid ID);
    void Update();
    void Delete();
}

第三次,在我看来Read应该是一种工厂方法。在当前代码中,您需要首先实例化Appointment对象,然后才能调用Read方法来检索另一个Appointment对象。从课堂设计的角度来看,这对我来说似乎是错误的。

如何从基类/接口中取出Read并将其作为静态方法提供给所有派生/实现类?例如:

public class Appointment : IModel
{
    public static Appointment Read(Guid ID)
    {
        return new Appointment()
               {
                   ...
               };
    }
}

您还可以考虑将Read移动到静态(工厂)类中;然而,它必须足够聪明才能知道它应该返回什么样的物体。这可以工作,例如如果您的数据库中有一个表将GUID映射到相应的对象类型。

修改:上面的最后一条建议是:

第三,如果到目前为止这是正确的,那么下一个问题是Read是否应该是静态方法。如果是,则可以将其设置为static并移动到静态Model类中。然后,该方法就像一个工厂方法,从DB构建IModel个对象:

Guid guid = ...;
IModel someModel = Model.Read(guid);

答案 5 :(得分:1)

这个设计有点古怪。

无论Model类是否被模板化,在Read方法上放置模板参数作为实例方法都没有多大意义。

通常你会有类似Greg D发布的内容。

答案 6 :(得分:1)

如果public abstract T Read<T>(Guid ID);的{​​{1}}只返回Model的派生类型,请考虑将签名更改为

Model

答案 7 :(得分:1)

在您的约会课程中

添加此

public class Appointment<T> : Model where T : Appointment

答案 8 :(得分:0)

你也可以打电话: var x = myModel.Read<Appointment>();