Определение подтипа объекта после его создания - PullRequest
0 голосов
/ 30 октября 2018

Допустим, у меня есть следующие классы:

public class Animal {
  public string name;
  public int age;
  public bool canBark;
}

public class Dog : Animal {
  }

И создать экземпляр Animal следующим образом:

Animal a = new Animal();

Есть ли какой-либо способ преобразования этого экземпляра в экземпляр Dog после его создания? Или я вынужден признать, что мой Animal - это Dog, когда я его создаю? Причина, по которой у меня возникает эта проблема, заключается в том, что мой экземпляр создается путем вызова фабричного класса, который возвращает тип, основанный на файле JSON, который я передаю. Значение атрибута canBark вычисляется путем просмотра нескольких, казалось бы, не связанных полей в этом файле. Кажется, имеет смысл определить, что что-то является Dog, взглянув на поле canBark, а не на эти поля JSON.

public class AnimalFactory{
  public static Animal Create(JObject json){
    Animal a = new Animal();
    a.canBark = (json["someField"]["someOtherField"].ToString() == "abc")
    .....
}

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

Итак, каков наилучший способ определения типа путем переноса этого решения в создание экземпляра или есть какой-то другой способ?

Ответы [ 4 ]

0 голосов
/ 30 октября 2018

В таких языках, как Java и C #, тип объекта определяется при его создании. Вы можете объявить ссылку Animal и затем указать ее на Dog, но сам объект Dog всегда будет Dog. Пользователь объекта может не знать его фактический тип, и обычно лучше, если он этого не знает.

Animal a = new Dog();
a.onOwnerHome();

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

0 голосов
/ 30 октября 2018

Животное a = новое Животное ();

Вы никогда не должны делать это вообще, поскольку в любой неакадемической среде Animal является абстрактным типом.

Есть ли какой-либо способ преобразования этого экземпляра в экземпляр Dog после его создания?

Это должно быть очевидно, но new выделяет память. Если вы говорите, что выделяете память для Animal, но позже решите, что это на самом деле Dog, вы не сможете добавить к выделенной памяти.

Другими словами, вы есть то, что вы есть. Если завтра вы решите, что хотите стать обезьяной, а не (предположительно) человеком, вы не станете буквально обезьяной.

Возможно, менее очевидным, если вы совсем не знакомы с тем, как работают виртуальные функции, является то, что виртуальная таблица инициализируется во время new, поэтому компилятору необходимо знать, какие указатели функций назначать в момент создания .

тип, основанный на файле JSON, который я передаю

Для этого предназначены словари или динамические объекты (ExpandoObject в C #). Или, что еще лучше, используйте подходящие библиотеки, которые справятся с этим, например, печально известную библиотеку Newtonsoft JSON .

0 голосов
/ 30 октября 2018

У вас может быть фабричный метод, имеющий тип возврата Animal, но создающий и возвращающий Dog. Но вы не можете преобразовать Animal в Dog после его создания.

Обратите внимание, что истинное преобразование и приведение типов - это не одно и то же. Даже если оператор приведения одинаков для обоих в C #. Если у вас есть переменная Animal a = new Dog();, вы можете выполнить правильное приведение Dog d = (Dog)a;. Но a должен ссылаться на Dog объект. Если ему присваивается Cat, то при кастинге возникает исключение.

Лучшее, что вы можете сделать, если вы действительно хотите преобразовать Animal в Dog, это добавить конструктор к Dog, принимая животное в качестве аргумента (C #):

public Dog(Animal a)
{
    name = a.name;
    age = a.age;
    canBark = true;
}

Тогда вы можете создать собаку с:

Animal a = new Animal();
// Initialize animal

Dog dog = new Dog(a);

Но вы не можете сделать это без создания нового объекта. Поэтому создайте правильный тип с самого начала на фабрике:

public class AnimalFactory
{
    public static Animal Create(JObject json)
    {
        string animalType = Get animal type string from json;

        Animal a;
        switch (animalType) {
            case "dog":
                var dog = new Dog();
                // Fill dog specific stuff.
                a = dog;
                break;
            case "cat":
                var cat = new Cat();
                // Fill cat specific stuff.
                a = cat;
                break;
            default:
                return null;
        }
        // Fill stuff common to all animals into a.
        return a;
    }
}

Вероятно, нет смысла создавать экземпляр класса Animal напрямую. Поэтому я бы объявил это абстрактным.


Вы также можете иметь конструкторы с параметром JObject в классах животных для делегирования инициализации полей.

public abstract class Animal
{
    public Animal(JObject json)
    {
        // Initialize common fields.
    }

    public string name;
    public int age;
    public bool canBark;
}

public class Dog : Animal
{
    public Dog(JObject json)
        : base(json) // Pass the json object to the Animal constructor
    {
        // Initialize dog specific fields.
    }
}

Завод становится

public class AnimalFactory
{
    public static Animal Create(JObject json)
    {
        string animalType = Get animal type string from json;
        switch (animalType) {
            case "dog":
                return new Dog(json);
            case "cat":
                return new Cat(json);
            default:
                return null;
        }
    }
}
0 голосов
/ 30 октября 2018

Есть ли какой-либо способ преобразования этого экземпляра в экземпляр Dog после его создания?

Нет. Тип объекта никогда не может быть изменен после создания в Java или .NET. Вам нужно изменить заводской код, чтобы создать экземпляр правильного класса. Не сразу понятно, как часть canBark относится к остальной части вопроса, но, по сути, вам нужно иметь где-то , который решает, какой тип объекта создавать на основе содержимого JSON - или изменить свой дизайн так, он может использовать один и тот же тип объекта во всех случаях.

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