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

2020-06-11 13:44发布

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. 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". 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.

What am I missing? Can anyone give me some code samples?

9条回答
萌系小妹纸
2楼-- · 2020-06-11 13:55

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.

Second, as your implementation of Appointment.Read suggests, you could change the method signature of Read to return a model object.

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. In your current code, you need to first instantiate an Appointment object before you can call the Read method to retrieve another Appointment object. 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? 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; however, it would then have to be smart enough to know what kind of object it should return. This would work e.g. if you had a table in your DB that would map a GUID to the corresponding object type.

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. If so, it could be made static and be moved into a static Model class. The method would then act like a factory method that builds IModel objects from a DB:

Guid guid = ...;
IModel someModel = Model.Read(guid);
查看更多
ら.Afraid
3楼-- · 2020-06-11 13:59

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.

Usually you'd have something like what Greg D posted.

查看更多
疯言疯语
4楼-- · 2020-06-11 14:00

You need to box and cast. I wonder though why this method is generic?

return (T)(object)appointment;
查看更多
放荡不羁爱自由
5楼-- · 2020-06-11 14:02

in your Appointment class add this

public class Appointment<T> : Model where T : Appointment
查看更多
Melony?
6楼-- · 2020-06-11 14:03

If public abstract T Read<T>(Guid ID); of Model will only ever return derived types of Model, consider changing the signature to

public abstract class Model
{
    public abstract void Create();
    public abstract Model Read(Guid ID);  //<--here
    public abstract void Update();
    public abstract void Delete();
}
查看更多
Animai°情兽
7楼-- · 2020-06-11 14:06

Would this work?

public abstract T Read<T>(Guid ID) where T : IAppointment;
查看更多
登录 后发表回答