C#Generics – 如何返回特定类型?

也许我说这一切都错了。

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

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

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

 public override T Read(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; } 

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

我错过了什么? 谁能给我一些代码示例?

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

 class Model { public abstract T Read(Guid ID); } class Appointment : Model { public override Appointment Read(Guid ID) { } } 

现在你的子类都是强类型的。 当然,权衡是你不再拥有一个基类。 ModelModel 。 我一般都没有发现这是一个问题,因为它没有什么共同的function – 接口是相似的,但它们都使用不同的类型。

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

 interface IModelEntity { object Read(Guid ID); } class Model : 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 { protected override Appointment OnRead(Guid ID) { /* Do Read Stuff */ } } 

这会有用吗?

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

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

 return (T)(object)appointment; 

你必须首先转向object然后转向T 原因在于该object位于inheritance链的顶端。 从AppointmentT没有直接关联; 因此你必须回溯到object ,然后找到回到T

我提供了这个答案来解释为什么返回陈述不会起作用,除非它被双重演绎 – 并且支持Chaos&Greg给出的答案

首先 ,我建议你把基类变成一个接口。 如果这是你的选择,这也会减少稍微混乱的代码,因为你可以摆脱接口声明中的abstractpublic关键字,并省略实现类中的override

其次 ,正如您对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); 

这个设计有点古怪。

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

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

如果public abstract T Read(Guid ID); Model只会返回派生类型的Model ,考虑将签名更改为

 public abstract class Model { public abstract void Create(); public abstract Model Read(Guid ID); //<--here public abstract void Update(); public abstract void Delete(); } 

在您的约会课程中添加此项

 public class Appointment : Model where T : Appointment 

你也可以调用: var x = myModel.Read();