Как вернуть в методе абстрактного класса наследуемый класс C#
У меня есть класс Parent и наследуемые от него Childs. У них есть собственные поля и методы. Но если использовать такой код, то доступа к ним нет:
abstract class Parent{
public Parent Open()
{
*действие*
return this;
}
}
class Child : Parent{
public int num;
}
Теперь я хочу вызвать new Child().Open().num, но оно не работает.
Что сделать?
Ответы (3 шт):
Нет, такого язык 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> и получить исключение.
Как вариант заменить поле на абстрактное свойство:
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);
}
}
Если у вас 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);
}
}
Тип придется переопределять в каждом наследнике, по необходимости.В более старых версиях языка работать не будет.