C # наследование приведение одного ребенка к другому - PullRequest
4 голосов
/ 04 декабря 2009

У меня есть такая простая структура: 1 родитель и два разных потомка.

public class Parent{}

public class ChildA : Parent{}

public class ChildB : Parent{}

У меня есть объект objA типа ChildA, который я хочу привести к ChildB. Мой наивный подход говорит:

ChildA objA = new ChildA();

ChildB objB = (ChildB)objA;

Но это невозможно напрямую - почему? Это потому, что мне нужно реализовать некоторые функции или потому что мой наивный подход неверен?

С уважением, Каспер

Ответы [ 8 ]

10 голосов
/ 04 декабря 2009

Это невозможно, потому что объект objA относится к , а не a ChildB. Другими словами, вот пример того, что вы пытаетесь сделать:

 string x = "hi";
 FileStream y = (FileStream) x;

У них обоих общий родитель - System.Object - но это совершенно разные классы. Что бы вы ожидали, если бы попытались прочитать с y?

Предположим, что у вашего ChildB типа есть какое-то поле, специфичное для этого типа - что вы ожидаете от значения этого поля после приведения objA?

Почему вы хотите сделать вид, что ChildA на самом деле ChildB? Не могли бы вы добавить метод в родительский класс, который делает то, что вы хотите? Добавьте метод в ChildA, например так:

ChildB ToChildB()

выполнить соответствующее преобразование?

3 голосов
/ 04 декабря 2009

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

Вам необходимо реализовать явный или косвенный оператор ChildA (или ChildB).

class ClassA
{
    public string Property1 { get; set; }
}

class ClassB
{
    public string Property2 { get; set; }

    public static implicit operator ClassB(ClassA classA)
    {
        return new ClassB() { Property2 = classA.Property1 };
    }
}

или

class ClassA
{       {
    public string Property1 { get; set; }

    public static explicit operator ClassB(ClassA classA)
    {
        return new ClassB() { Property2 = classA.Property1 };
    }
}

class ClassB
{
    public string Property2 { get; set; }
}

А после реализации разговоров операторы будут нормально работать со следующим кодом:

var a = new ClassA() {Property1 = "test"};
ClassB b = (ClassB)a;
Console.WriteLine(b.Property2); // output is "test"

В первом случае вы можете явно пропустить преобразование типов и написать так:

var a = new ClassA() {Property1 = "test"};
ClassB b = a;

И, наконец, если вы хотите синхронизировать только свойства родительского класса, вы можете написать конвертер непосредственно в родительском:

class Parent
{
    public string ParentProperty { get; set; }
    public static T1 Convert<T1>(Parent obj) where T1 : Parent, new()   
    {
    var result = new T1();
    result.ParentProperty = obj.ParentProperty;
    return result;
    }
}

Использование (потомки родителей ClassA и ClassB):

var a = new ClassA();
a.ParentProperty = "test";
ClassB b = Parent.Convert<ClassB>(a);
Console.WriteLine(b.ParentProperty); // output is "test"
2 голосов
/ 04 декабря 2009

Вы не можете этого сделать, потому что ChildA не является ChildB (вы можете передавать только из ChildA или ChildB в Parent, или понижать из Parent в ChildB или ChildA, в C # такой вещи нет как Sidecast в *)

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

0 голосов
/ 18 ноября 2014

Я почти уверен, что придумал способ симулировать это, что может быть полезно в какой-то момент. А именно:

  • Унаследовать от Dictionary или IDictionary и реализовать его, если вам нужно другое базовое наследование
  • Храните свойства в двух местах - словаре и реальном поле
  • Оставьте третье логическое поле, которое указывает, было ли установлено настоящее поле
  • Если установлено реальное поле, возьмите реальное поле
  • Если это не так, взять значение словаря (а также присвоить его реальному полю и пометить)
  • Если значение словаря отсутствует, действуйте так, как будто свойство не существует
  • Добавить конструктор, который принимает словарь и заполняет его значениями из словаря

Теперь вы можете взять CatLikeObject, унаследованный от этого базового класса, и с помощью конструктора (привести кота в словарь) сгенерировать идентичный DogLikeObject (который будет лаять вместо мяу, но все равно будет называться "Puss").

Недостатки? Свойства занимают ОЧЕНЬ больше места, и большая часть безопасности типов переносится на время выполнения, не говоря уже о любых потерях производительности, которые могут быть (и определенно будут). Преимущества? Если вам нужно временно обращаться с кошкой как с собакой, вы можете это сделать.

public class CSharepointStoredResults : Dictionary<string, object>
{

    public CSharepointStoredResults(Dictionary<string, object> SourceDict = null) 
    {
        // Populate class dictionary from passed dictionary. This allows for some degree of polymorphism sideways.
        // For instance it becomes possible to treat one CSharepointStoredResults as another (roughly like treating
        // a cat as a dog
        foreach (string key in SourceDict.Keys) { this.Add(key, SourceDict[key]); }
    }

    public Type MyType 
    {
        get {
            if (!__MyType && !this.ContainsKey(bObj.GetPropertyNameFromExpression(() => this.MyType)))
            {
                // Neither a dictionary nor a field set
                // return the field
            }
            else if (!__MyType)
            {
                // There is a dictionary entry, but no volatile field set yet.
                __MyType = true;
                _MyType = this[bObj.GetPropertyNameFromExpression(() => this.MyType)] as Type;
            }
            else 
            {
                // Volatile value assigned, therefore the better source. Update the dictionary  
                this[bObj.GetPropertyNameFromExpression(() => this.MyType)] = _MyType;
            }
            return _MyType;
        }
        set {
            // Verify the value is valid...
            if (!(value.IsInstanceOfType(typeof(CSharepointStoredResults))))
                throw new ArgumentException("MyType can only take an instance of a CSharePointResults object");
            _MyType = value;
            this[bObj.GetPropertyNameFromExpression(() => this.MyType)] = value;
        }
    }
    private volatile Type _MyType;
    private volatile bool __MyType;

}

0 голосов
/ 04 декабря 2009

Как говорят другие, ChildA - это не ChildB. Если ChildA и B имеют одинаковые свойства / функции, вы должны сделать:

public class Parent{}
public class Child : Parent{}

Child objA = new Child();
Child objB = objA;

Но я предполагаю, что это только пример, у вас есть реальный живой пример, почему вы хотите достичь чего-то подобного?

0 голосов
/ 04 декабря 2009

ChildA и ChildB - это разные типы, которые имеют одного и того же родителя. Таким образом, вы можете рассматривать экземпляры как ChildA, так и ChildB как их основу, Parent, но, поскольку они относятся к разным типам, вы не можете привести один к другому.

0 голосов
/ 04 декабря 2009

То, что вы пытаетесь сделать, не сработает.

Вы можете использовать objA только для его базового класса (Parent) или для любого общего интерфейса, который могли бы реализовывать ChildA и ChildB.

Представьте на мгновение, что ChildB определил метод с именем Foo. Как ваш экземпляр objA будет иметь дело с кем-то, кто зовет Фу? Очевидно, это не могло сработать.

0 голосов
/ 04 декабря 2009

objA - это НЕ типа ChildB, даже если оба являются "детьми" из класса Parent.

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