也许我说这一切都错了。
我有一堆派生自“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)”,那么编译器会说我没有在子类中实现抽象方法。
我错过了什么?谁能给我一些代码样本?
答案 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
位于继承链的顶端。 Appointment
与T
之间没有直接关联;因此,您必须回溯到object
,然后返回T
。
我提供了这个答案来解释为什么返回陈述不会起作用,除非它被双重演绎 - 并且支持Chaos&amp; amp;格雷格
答案 4 :(得分:2)
首先,我建议您将基类转换为界面。如果这是一个选项,这也会减少稍微混乱的代码,因为你可以摆脱接口声明中的abstract
和public
关键字,并省略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>();