Подтвердить что ты не робот

Перенос производного класса на базовый

Class A { }
Class B : A { }

B ItemB = new B();
A ItemA = (A)B;

Console.WriteLine(ItemA.GetType().FullName);

Можно ли сделать что-то подобное выше, и распечатать тип компилятора типа A вместо типа B. В принципе, возможно ли навсегда лить объект, чтобы он "потерял" все производные данные?

4b9b3361

Ответ 1

То, о чем вы просите, невозможно по двум причинам:

  • ItemA.GetType() не возвращает тип времени компиляции переменной ItemA - он возвращает тип времени выполнения объекта, на который ссылается ItemA.
  • Невозможно сделать (A)B результатом преобразование, изменяющее представление (т.е. новый объект A), потому что пользовательские операторы преобразования (ваша единственная надежда здесь) не могут преобразовать из производных в базовые классы. Вы просто получите нормальное, безопасное, ссылочное преобразование.

В стороне, что вы просите, очень странно; можно было бы подумать, что вы очень сильно пытаетесь нарушить принцип Лискова. Здесь почти наверняка серьезный недостаток дизайна, который вы должны решить.

Если вы все еще хотите это сделать; вы можете написать метод, который вручную создает A из B путем добавления A и последующего копирования данных. Это может существовать как ToA()  instance-method на B.

Если вы охарактеризовали эту проблему как "Как мне построить A из существующего A?", это имеет гораздо больший смысл: создайте конструктор-копию на A, чья декларация выглядит как public A(A a){...}, что агностически относится к деталям, относящимся к подклассам. Это дает вам общее средство для создания A из существующего экземпляра A или одного из его подклассов.

Ответ 2

Недавно я столкнулся с этим переносом старого проекта в Entity Framework. Как уже упоминалось, если у вас есть производный тип от объекта, вы не можете сохранить его, а только базовый тип. Решение было методом расширения с отражением.

    public static T ForceType<T>(this object o)
    {
        T res;
        res = Activator.CreateInstance<T>();

        Type x = o.GetType();
        Type y = res.GetType();

        foreach (var destinationProp in y.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
        {
            var sourceProp = x.GetProperty(destinationProp.Name);
            if (sourceProp != null)
            {
                destinationProp.SetValue(res, sourceProp.GetValue(o));
            }
        }

        return res;
    }

Это не слишком аккуратно, поэтому используйте это, если у вас действительно нет другого варианта.

Ответ 3

Для развлечения, если вы хотите потерять все полученные данные, вы можете сделать это:

   class Program
    {
        [DataContract(Name = "A", Namespace = "http://www.ademo.com")]
        public class A { }
         [DataContract(Name = "A", Namespace = "http://www.ademo.com")]
        public class B : A  {
             [DataMember()]
             public string FirstName;
        }  

    static void Main(string[] args)
    {
        B itemB = new B();
        itemB.FirstName = "Fred";
        A itemA = (A)itemB; 
        Console.WriteLine(itemA.GetType().FullName);
        A wipedA = WipeAllTracesOfB(itemB);
        Console.WriteLine(wipedA.GetType().FullName);
    }

    public static A WipeAllTracesOfB(A a)
    {
        DataContractSerializer serializer = new DataContractSerializer(typeof(A));

        using (MemoryStream ms = new MemoryStream())
        {
            serializer.WriteObject(ms, a);
            ms.Position = 0;

            A wiped = (A)serializer.ReadObject(ms);

            return wiped;
        }
    }
}

Если вы используете отладчик, вы увидите, что FirstName все еще хранится в поле FirstName, когда оно передается в A, когда вы получаете A обратно из WipeAllTracesOfB, нет ни одного FirstName или любого следа B.

Ответ 4

(это, вероятно, очевидно, но...)

re: принятый ответ от @Ani:

Если вы все еще хотите это сделать; вы можете написать метод, который вручную создает A из B, создавая A и затем копируя данные. Это может существовать как экземпляр-метод ToA() на B.

Или используйте Automapper, чтобы скопировать данные:

Mapper.CreateMap<ChildClass, BaseClass>();
// ...later...
var wipedInstance = Mapper.Map<BaseClass>(instanceOfChildClass);

то wipedInstance.GetType() будет typeof(BaseClass)

Ответ 5

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

Обратите внимание, что при использовании переменной ItemA, указывающей на экземпляр класса B, вы можете получить доступ только к полям и методам, определенным в классе A, используя переменную ItemA. Есть очень мало мест, где фактически можно заметить тот факт, что ItemA указывает на экземпляр чего-то другого, кроме класса A. - виртуальные методы, переопределенные в дочерних классах, - это один случай, а также операции с самим типом среды выполнения, такие как GetType().

Если вы задаете этот вопрос, потому что какой-то фрагмент кода терпит неудачу, когда вы отправляете ему экземпляр класса B, когда он ожидает экземпляр класса A, похоже, что вы должны более внимательно изучить этот код, чтобы видеть, что он делает неправильно. Если он тестирует GetType.LastName, то он сломан и braindead. Если он тестирует x IS A, то передача экземпляра B пройдет, и все будет хорошо.

Ответ 6

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

A ItemA = (A)ItemB; говорит take ItemB, обрабатывайте его так, как если бы он был типа A, и называет его ItemA

Когда вы вызываете ItemA.GetType(), вы запрашиваете реальный тип, а не только тип, к которому он относится, и реальный тип B

(Я сменил ваш пример кода на то, что, я думаю, имел в виду, поскольку то, что у вас есть, не будет компилироваться)