Полимофизм в C#, как превратить родительский объект в дочерний объект в методе? - PullRequest
1 голос
/ 03 апреля 2020
public class Program
{
    public static void Main()
    {
        var type = new C().getType();
        type.Foo();
    }

    public class A 
    {
        public void Stop() 
        {
           // do something
        }
    }

    public class B : A
    {
        public void Foo() 
        {
            Console.WriteLine("Foo");   
        }
    }

    public class D : A
    {
        public void Bar() 
        {
            Console.WriteLine("Bar");   
        }
    }   

    public class C
    {
        public A getType()
        {
            if (some condition)
               return new B();
            if (some condition)
               return new D();
            return A();
        }   
    }
}

-EDIT-

Я обновил код, так что теперь у нас есть два дочерних класса B и D, оба имеют разные методы внутри, однако, так как они наследуют A, по крайней мере оба имеют доступ к Stop() методу. Проблема создания абстрактного метода Foo() и Bar() внутри родительского класса A заключается в том, что B не должен иметь доступа к Bar(), а D не должен иметь доступа к Foo(), но делая абстрактный метод, им нужно будет реализовать их оба.

Я знаю, что могу проверить тип возвращаемого объекта внутри метода Main() и затем привести его к этому типу. Но это не будет удобно, так как в будущем у меня будет больше потомков A.

Можно ли использовать dynamic в моей ситуации? Потому что это решит проблему и будет очень удобно.

public class C
{
    public dynamic getType()
    {
        if (some condition)
           return new B();
        if (some condition)
           return new D();
        return A();
    }   
}

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Вы почти правильно поняли:

public class Program
{
    public static void Main()
    {
        var type = new C().getType();
        type.Foo();
    }

    public abstract class A 
    {
        public abstract void Foo();
    }

    public class B : A
    {
        public override void Foo() 
        {
            Console.WriteLine("Foo");   
        }
    }

    public class C
    {
        public A getType()
        {
            return new B(); 
        }   
    }
}

Вам нужен метод Foo в A, который B может переопределить. Я сделал A.Foo абстрактным, что означает, что нам не нужно определять базовую реализацию A.Foo. Класс A объявляется абстрактным, чтобы предотвратить попытки создания экземпляра этого класса.

Если вы хотите, чтобы производные классы наследовали базовую реализацию Foo от A, тогда объявите Foo как виртуальный в A. Итак, в следующем Например, класс B переопределяет базовый Foo, тогда как класс C наследует базовый Foo:

public class Program
{
    public static void Main()
    {
        A a1 = new B();
        a1.Foo();  // Outputs "B.Foo".
        A a2 = new C();
        a2.Foo();  // Outputs "A.Foo".
    }

    public abstract class A
    {
        public virtual void Foo()
        {
            Console.WriteLine("A.Foo");
        }
    }

    public class B : A
    {
        public override void Foo()
        {
            Console.WriteLine("B.Foo");
        }
    }

    public class C : A
    {
    }
}
0 голосов
/ 03 апреля 2020

Вы путаете тип переменной и тип объекта времени выполнения, на который она будет указывать (учитывая тип ссылки, с типами значений, такого различия нет).

Когда вы объявляете переменная, вы определяете тип переменной :

Foo a = ... //a is of type Foo at compile time.

То, что вы присваиваете этой переменной, не имеет значения вообще (если вы не используете переменные с неявным типом с var)

Но, учитывая следующий код:

object o = new Foo();

Теперь вы можете увидеть разницу; переменная o имеет тип object, но объект времени выполнения, на который она будет указывать, имеет тип string.

В своем вопросе вы спрашиваете, почему вы не можете сделать следующее:

var stringLength = o.Length; //o is a string isn't it?

Почему? Разве o не действительно string. Да, но эта информация доступна только во время выполнения , компилятор ничего не знает о том, что произойдет во время выполнения, он знает только, что o имеет тип object, а object не имеет Length имущество. Единственное, в чем убедится компилятор, это то, что типы совместимы; это позволит вам присвоить Giraffe типизированной переменной Animal, но не позволит вам присвоить Car.

Если у вас все ясно, то вы поймете, почему это не имеет смысла:

Но я явно возвращал B внутри метода getType (), почему у него нет доступа к Foo ()?

Поскольку метод getType тип возврата A не B, не C и не D.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...