Как вернуть в методе абстрактного класса наследуемый класс C#

У меня есть класс Parent и наследуемые от него Childs. У них есть собственные поля и методы. Но если использовать такой код, то доступа к ним нет:

abstract class Parent{
   public Parent Open()
   {
      *действие*
      return this;
   }
}
class Child : Parent{
public int num;
}

Теперь я хочу вызвать new Child().Open().num, но оно не работает. Что сделать?


Ответы (3 шт):

Автор решения: VladD

Нет, такого язык C# покамест не умеет, хотя предложение о поддержке таких конструкций обсуждалось. Open возвращает Parent, т. к. не знает ничего о Child.

Вам придётся либо явно преобразовывать тип к Child, либо отказаться от fluent-синтаксиса:

abstract class Parent
{
    public void Open()
    {
        // *действие*
    }
}

class Child : Parent
{
    public int num;
}
var c = new Child();
c.Open();
c.num = 1;

Ещё одно, на мой вкус избыточно сложное решение, состоит в применении идиомы Curiously Recurring Template Pattern:

class Parent<TSelf> where TSelf : Parent<TSelf>
{
    public TSelf Open()
    {
        // *действие*
        return (TSelf)this;
    }
}

class Child : Parent<Child>
{
    public int num;
}

При этом вы таки сможете написать

new Child().Open().num = 1;

Но я не рекомендовал бы идти этим путём, он открывает ящик Пандоры. Например, никто не помешает вам написать class Evil : Parent<Child> и получить исключение.

→ Ссылка
Автор решения: SelfishCrawler

Как вариант заменить поле на абстрактное свойство:

abstract class Parent
{
    public abstract int num { get; set; }
    public Parent Open() => this;
}

class Child : Parent
{
    public override int num { get; set; }
}

public class Program
{
    public static void Main()
    {
        Child cld = new Child();
        cld.num = 4;
        Console.WriteLine(cld.Open().num);
    }
}

Вариант с виртуальным свойством:

abstract class Parent
{
    public virtual int num { get; set; }
    public Parent Open => this;
}

class Child : Parent
{
}

public class Program
{
    public static void Main()
    {
        Child cld = new Child();
        cld.num = 4;
        Console.WriteLine(cld.Open.num);
    }
}
→ Ссылка
Автор решения: PashaPash

Если у вас C# 9 (.NET 5), то можно использовать Covariant Return. Объявить метод базового класса как virtual, и переопределить тип возвората в Child:

abstract class Parent
{
    public virtual Parent Open()
    {
       // *действие *
       return this;
    }
}
class Child : Parent
{
    public int num = 5;

    public override Child Open() => (Child)base.Open();
}
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new Child().Open().num);
    }
}

Тип придется переопределять в каждом наследнике, по необходимости.В более старых версиях языка работать не будет.

→ Ссылка