简体   繁体   English

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

[英]C# Generics - How do I return a specific type?

Maybe I'm going about this all wrong. 也许我说这一切都错了。

I have a bunch of classes that derive from the "Model" class, a base class with a bunch of common properties and methods. 我有一堆派生自“Model”类的类,一个带有一堆常见属性和方法的基类。 I want them all to implement a set of functionality: 我希望他们都实现一组功能:

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

Then I implement it in a child class like "Appointment" like so: 然后我在像“约会”这样的子类中实现它,如下所示:

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

This throws an exception "Can't implicitly convert type 'Appointment' to T". 这会引发异常“无法将类型'约会'隐式转换为T”。 If I change the method's signature to "public override Appointment Read(Guid ID)", then the compiler says that I've not implemented the abstract method in the child class. 如果我将方法的签名更改为“public override Appointment Read(Guid ID)”,那么编译器会说我没有在子类中实现抽象方法。

What am I missing? 我错过了什么? Can anyone give me some code samples? 谁能给我一些代码示例?

It looks like you could use a generic base class! 看起来你可以使用通用基类! Consider something like the following: 考虑以下内容:

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

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

Now your subclasses are all strongly typed. 现在你的子类都是强类型的。 Of course, the tradeoff is that you no longer have a single base class. 当然,权衡是你不再拥有一个基类。 A Model<Appointment> isn't the same thing as a Model<Customer> . Model<Appointment>Model<Customer> I have not generally found this to be a problem, though, because there's little common functionality-- the interfaces are similar, but they all work with different types. 我一般都没有发现这是一个问题,因为它没有什么共同的功能 - 接口是相似的,但它们都使用不同的类型。

If you'd like a common base, you can certainly cheat and implement an object -based interface that does the same general tasks. 如果你想要一个共同的基础,你当然可以欺骗并实现一个基于object的界面来完成相同的一般任务。 Eg, something in the spirit of (untested, but the idea's there): 例如,某种精神(未经测试,但想法在那里):

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 */ }
}

这会有用吗?

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

You need to box and cast. 你需要盒装和施法。 I wonder though why this method is generic? 我想知道为什么这种方法是通用的?

return (T)(object)appointment;

You must cast to object first then to T . 你必须首先转向object然后转向T The reason is rooted in that object is at the top of the inheritance chain. 原因在于该object位于继承链的顶端。 There is no direct correlation from Appointment to T ; AppointmentT没有直接关联; therefore you have to backtrack to object , then find your way back to T . 因此你必须回溯到object ,然后找到回到T

I provided this answer to give an explanation of why the return statement would not work unless it was doubly cast -- and in support of the answers given by Chaos & Greg 我提供了这个答案来解释为什么返回陈述不会起作用,除非它被双重演绎 - 并且支持Chaos&Greg给出的答案

First , I'd suggest you turn your base class into an interface. 首先 ,我建议你把基类变成一个接口。 If that's an option for you, this will also reduce in slightly less-cluttered code, as you can get rid of the abstract and public keywords in the interface declaration and omit the override in the implementing classes. 如果这是你的一个选项,这也会减少稍微混乱的代码,因为你可以摆脱接口声明中的abstractpublic关键字,并省略实现类中的override

Second , as your implementation of Appointment.Read suggests, you could change the method signature of Read to return a model object. 其次 ,正如您对Appointment.Read的实现所暗示的那样,您可以更改Read的方法签名以返回模型对象。

Both suggested changes would result in the following: 两个建议的更改都会导致以下结果:

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

Third , it seems to me that Read should really be a factory method. 第三 ,在我看来, Read应该是一种工厂方法。 In your current code, you need to first instantiate an Appointment object before you can call the Read method to retrieve another Appointment object. 在当前代码中,您需要首先实例化一个Appointment对象,然后才能调用Read方法来检索另一个Appointment对象。 This seems wrong to me, from a class design perspective. 从课堂设计的角度来看,这对我来说似乎是错误的。

How about taking Read out of the base class/interface and providing it as a static method in all derived/implementing classes? 如何将Read从基类/接口中取出并在所有派生/实现类中将其作为静态方法提供? For example: 例如:

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

You could also consider moving Read into a static (factory) class; 您还可以考虑将Read移入静态(工厂)类; however, it would then have to be smart enough to know what kind of object it should return. 然而,它必须足够聪明才能知道它应该返回什么样的物体。 This would work eg if you had a table in your DB that would map a GUID to the corresponding object type. 这将有效,例如,如果您的数据库中有一个表将GUID映射到相应的对象类型。

Edit : The last suggestion above used to be this: 编辑 :上面的最后一个建议是这样的:

Third, if this is correct so far, the next question would be whether or not Read should be a static method instead. 第三,如果到目前为止这是正确的,那么接下来的问题是Read是否应该是静态方法。 If so, it could be made static and be moved into a static Model class. 如果是这样,它可以变为static并移动到静态Model类中。 The method would then act like a factory method that builds IModel objects from a DB: 然后,该方法就像一个从DB构建IModel对象的工厂方法:

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

There is something funky about this design. 这个设计有点古怪。

Regardless of whether or not the Model class is templated, putting a template parameter on the Read method doesn't make a lot of sense as an instance method. 无论Model类是否是模板化的,在Read方法上放置模板参数作为实例方法都没有多大意义。

Usually you'd have something like what Greg D posted. 通常你会有类似Greg D发布的内容。

If public abstract T Read<T>(Guid ID); 如果是public abstract T Read<T>(Guid ID); of Model will only ever return derived types of Model , consider changing the signature to 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();
}

在你的Appointment课程中添加这个

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM