Может ли метод базового класса возвращать тип производного класса?

основываясь на других сообщениях, которые я прочитал, кажется, что это может быть невозможно, но я думал, что опубликую то, что я пытаюсь сделать, и посмотрю, знает ли кто-нибудь о решении.

Я пытаюсь добавить метод "Clone ()" в классы, созданные из модели домена открытого доступа Telerik. Не проблема. Я смог выяснить, как добавить базовый класс к сгенерированным моделям сущностей, чтобы я мог идентифицировать эти классы по их базовому классу. (все сущности наследуются от базы класс!--11-->)

Я хочу, чтобы все эти классы моделей сущностей могли клонировать себя. Я нашел решение и для этого. (Объекты Глубокого Клонирования)

теперь у меня есть базовый класс, и я хочу добавить функцию Clone() к каждому классу, производному от этого базового класса. Так. .. кажется, что базовый класс-это естественное место для его размещения ... правильно?

public abstract class DBEntityBase
{
    /// <summary>
    ///     Gets a COPY of the instance in it's current state
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected T Clone<T>()
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(this));
    }
}

добавить защищенный универсальный клон() метод, потому что в уровень базового класса мы не знаем тип, который мы клонируем. Метод Clone () должен быть реализован самой моделью сущности, чтобы обеспечить клонируемый конкретный тип.

public partial class DeliverableEntity
{
    public new DeliverableEntity Clone()
    {
        return this.Clone<DeliverableEntity>();
    }
}

это работает нормально, но не гарантия что производные классы будут публично выставлять метод Clone (), поэтому я добавил абстрактный клон() метод к основанию, которое будет требуются производный класс для реализации a метод public Clone ().

public abstract class DarkRoomDBEntityBase
{
    /// <summary>
    ///     Gets a COPY of the instance in it's current state
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected T Clone<T>()
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(this));
    }

    /// <summary>
    ///     Gets a deep COPY of the entity instance
    /// </summary>
    /// <returns></returns>
    public abstract DBEntityBase Clone();
}

Я даю ему возвращаемый тип самого базового класса; каждая реализация должна возвращать значение, которое придерживается DBEntityBase, что, конечно, делают все производные классы. Поскольку метод clone() возвращает тип самого производного класса ... кажется, есть смысл, что это сработает.

DeliverableEntity originalEntity = new DeliverableEntity();
DeliverableEntity clonedEntity   = originalEntity.Clone();

при построении, однако, я получаю ошибку ..

'DeliverableEntity' не реализует унаследованный абстрактный член 'DBEntityBase.Clone()'

предположительно из-за типа возврата.

Я знаю, что я мог бы просто поместить метод Clone() в отдельный служебный класс и не реализовывать его непосредственно в каждой модели сущности ... это помогло бы мне обойти мою проблему (и, вероятно, сохранить много кода реализации), но мне все еще интересно, почему это не сработает. Кажется, должен быть способ сделать это.

обновление

In ответ на первый ответ @Luann (спасибо) я сделал изменение на "override" ...

public partial class DeliverableEntity
{
    public override DeliverableEntity Clone()
    {
        return this.Clone<DeliverableEntity>();
    }
}

и теперь я получаю следующую ошибку ...

возвращаемый тип должен быть "DBEntityBase", чтобы соответствовать переопределенному члену 'DBEntityBase.Clone()'

решение

благодаря Flynn1179 я снова смог двигаться вперед. Я подумал, что мне нужно время, чтобы документировать то, что я сделал здесь для дальнейшего использования ..

вместо создания частичного класса для каждой модели сущности в ORM, реализуя абстрактный метод, я создал один метод расширения, как было предложено.

namespace DAL
{
    public partial class DeliverableEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }

    public partial class DeliverableItemEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }

    public partial class DeliverableItemAttrEntity : DBEntityBase
    {
         // ... Code generated from ORM 
    }
}

namespace DAL
{
    public static class EntityExtensionMethods
    {
        public static T Clone<T>(this T entity) where T: DBEntityBase
        {
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(entity));
        }
    }
}

некоторые вещи, чтобы отметить ...

  • поместите этот класс В то же пространство имен, что и модели сущностей. Если он находится в другом пространстве имен, вам нужно будет добавить это пространство имен, чтобы иметь доступ к методу.
  • я определил ограничение на общий тип только для и все классы, которые наследуют DBEntityBase класса. Поскольку я сделал все мои классы модели сущностей производными от этого базового класса, я знаю, что все они будут предоставлять этот метод, но также и любой класс, который не является производным от этого класса, не будет иметь этой возможности.
  • метод расширения и класс, который его содержит, должны быть статический

теперь классная часть заключается в том, что все сущности имеют доступ к функции ...

    // Create the original instances of the entities    
    DeliverableEntity origDeliverable       = new DeliverableEntity();
    DeliverableItemEntity origItem          = new DeliverableItemEntity();
    DeliverableItemAttrEntity origItemAttr  = new DeliverableItemAttrEntity();

    // now here's the magic 

    DeliverableEntity cloneDeliverable      = origDeliverable.Clone();
    DeliverableItemEntity cloneItem         = origItem.Clone();
    DeliverableItemAttrEntity cloneItemAttr = origItemAttr.Clone();

мне нравится это решение, поскольку оно имеет простоту базового класса, где реализация определена в одном месте (в то время как я рассматривал реализацию абстрактного метода индивидуально в каждом производном классе) плюс, поскольку оно связано с классом DBEntityBase и в том же пространстве имен, оно становится частью "контракта", определенного базовым классом, что означает, что я могу рассчитывать на его доступность, когда у меня есть класс, производный от DBEntityBase.

1 ответов


по многочисленным просьбам..

попробуйте метод расширения:

public T Clone<T>(this T obj) where T : DBEntityBase
{
  return /* insert code that creates clone here */
}

Я должен быть честным, я не думал, что это сработает, так как я ожидал, что C# не сможет точно определить, что это расширение. По-видимому, однако, это так!