开发者

C# inheritance casting one child to another

开发者 https://www.devze.com 2022-12-13 10:09 出处:网络
I have this simple structure: 1 parent, and two different childs. public class Parent{} public class ChildA : Parent{}

I have this simple structure: 1 parent, and two different childs.

public class Parent{}

public class ChildA : Parent{}

public class ChildB : Parent{}

I have an object objA of type ChildA, which I want to cast to ChildB. My naive approach says:

ChildA objA = new ChildA();

ChildB objB = (ChildB)objA;

But this is not directly possible - why? Is this because I need to implement some functions or because开发者_运维技巧 my naive approach is wrong?

Regards, Casper


It's not possible because the object objA refers to is not a ChildB. To put it another way, here's an example of what you're trying to do:

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

They both have a common parent - System.Object - but they're completely different classes. What would you expect to happen if you tried to read from y?

Suppose your ChildB type has some field which is specific to that type - what would you expect that field's value to be after casting objA?

Why do you want to pretend that a ChildA is actually a ChildB? Could you maybe add a method in the parent class which does what you want? Add a method in ChildA like this:

ChildB ToChildB()

to perform an appropriate conversion?


It is not possible to simply cast one object other type even if thay have one parent, because thay maybe have different interfaces.

You need to implement explicit or implitic operator of ChildA (or 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 };
    }
}

or

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; }
}

And after implementing conversings operators following code will work fine:

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

In first case you can omit explicitely type conversion and write just like this:

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

And finally if you want to synchronize only properties of parent class you can write converter directly in parent:

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;
    }
}

Using (ClassA and ClassB childs of Parent):

var a = new ClassA();
a.ParentProperty = "test";
ClassB b = Parent.Convert<ClassB>(a);
Console.WriteLine(b.ParentProperty); // output is "test"


You can't because ChildA is not a ChildB (you can only upcast from ChildA or ChildB to Parent, or downcast from Parent to ChildB or ChildA, there's no such thing as sidecasting in C#)

If you want to make the cast possible (a questionable endeavor, but well) you should implement an cast operator from ChildA to ChildB.


objA is NOT of type ChildB even if both are "children" from class Parent.


What you're trying to do won't work.

You can only case objA to it's base class (Parent) or to any common interface that ChildA and ChildB might implement.

Imagine, for a moment, that ChildB defined a method called Foo. How would your instance of objA deal with someone calling Foo? Clearly it couldn't work.


ChildA and ChildB are different types which share the same parent. Thus you can treat instances of both ChildA and ChildB as their base, Parent, but as they are different types you can't cast one to the other.


As others say ChildA is not ChildB. If ChildA and B have the same properties/functions then you should do:

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

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

But I gues this is only an example, you got a real live example why you want to achieve something like this?


I'm pretty sure I've come up with a way to simulate this, which might be useful at some point. Namely:

  • Inherit from Dictionary, or IDictionary and implement it if you need other base inheritance
  • Store properties in two locations - the dictionary and a real field
  • Keep a third boolean field that flags whether or not the real field has been set yet
  • If the real field has been set, take the real field
  • If it hasn't, take the dictionary value (as well as assigning it to the real field, and flagging)
  • If no dictionary value, act as though the property doesn't exist
  • Add a constructor that takes a Dictionary and populates this with the values from the Dictionary

You can now take a CatLikeObject inheriting from this base class, and by using the constructor (cast the cat to a Dictionary), generate an identical DogLikeObject (which will bark instead of meow, but still be called "Puss").

Disadvantages? Properties take up a LOT more space, and a lot of type safety gets shifted to runtime, let alone any performance loss that there may be (and there will definitely be some). Advantages? If you need to treat a Cat as a Dog temporarily, you can.

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

精彩评论

暂无评论...
验证码 换一张
取 消