“new” modifier causes base implementations to have NULL property values

久未见 提交于 2019-12-11 10:17:12

问题


I'm trying to use Polymorphism to have a derived class operate with a derived property instead of the base property. I'm not sure how to put it in clearer words, so here's an example with the output:

// setup output to demonstrate the scenario
static void Main(string[] args)
{
    var foo = new Foo();            
    var foobase = foo as FooBase;

    Console.WriteLine("Foo is null? {0}", foo == null);
    Console.WriteLine("Foo.Bar is null? {0}", foo.Bar == null);
    Console.WriteLine("FooBase is null? {0}", foobase == null);
    Console.WriteLine("FooBase.Bar is null? {0}", foobase.Bar == null);
    Console.ReadLine();            
}

// base and derived.  These represent my problem.
class BarBase { }
class Bar : BarBase { }

// Base implementation using base properties
class FooBase
{
    public virtual BarBase Bar { get; set; }
    public FooBase() { }
}

// Derived implementation using "new" to operate with the 
// derived the property
class Foo : FooBase
{
    public new Bar Bar { get; set; }
    public Foo() : base()
    {
        // populate our modified property with the derived class
        Bar = new Bar();
        //base.Bar = Bar; // I don't want to do this, but how can I avoid it?
    }
}

OUTPUT:

Foo is null? False
Foo.Bar is null? False
FooBase is null? False
FooBase.Bar is null? True

The problem is "FooBase.Bar" is NULL. I understand that the new modifier is hiding the base implementation, that's why I know I can make it work by adding the (commented out) line:

base.Bar = Bar; // I don't want to do this, but how can I avoid it?

Is there a better way? What am I missing?

Thanks in advance!


EDIT (6/7/11)

Adding a more specific example to my problem. Basically, there are abstract implementations for a Game and a Camera (very basic here...). The abstract Game uses the abstract Camera for basic work. The derived Game uses its derived Camera for its specialty work. In the end, I'd need both the derived and the abstract Cameras to work in their own scope.

I've given two different Camera implementations and two Game implementations to (hopefully) demonstrate the flexibility I'm looking for. Please note that Game1 uses a "hack" to force the base Camera to have a value- Game2 does not do this. Game1 has the behavior I want, but not the implementation I want.

class GameAndCameraExample
{
    static void Main(string[] args)
    {
        new Game1().Play();
        Console.WriteLine();
        new Game2().Play();

        Console.ReadLine();
    }

    // abstract camera
    abstract class CameraBase
    {
        public void SpecialCameraBaseMethod()
        {
            Console.WriteLine("SpecialCameraBaseMethod");
        }
    }

    // camera implementation
    class ChaseCamera : CameraBase
    {
        public void SpecialChaseCameraMethod()
        {
            Console.WriteLine("SpecialChaseCameraMethod");
        }
    }

    // a different camera implementation
    class FlightCamera : CameraBase
    {
        public void SpecialFlightCameraMethod()
        {
            Console.WriteLine("SpecialFlightCameraMethod");
        }
    }

    // abstract game
    abstract class GameBase
    {
        public virtual CameraBase Camera { get; set; }

        public GameBase() { }

        public virtual void Play()
        {
            Console.WriteLine("GameBase.Camera is null? {0}", Camera == null);
            if(Camera != null) // it will be null for Game2 :-(
                Camera.SpecialCameraBaseMethod(); 
        }
    }

    // awesome game using chase cameras
    class Game1 : GameBase
    {
        public new ChaseCamera Camera { get; set; }

        public Game1() : base()
        {
            Camera = new ChaseCamera();
            base.Camera = Camera; // HACK: How can I avoid this?
        }

        public override void Play()
        {
            Console.WriteLine("Game.Camera is null? {0}", Camera == null);
            Camera.SpecialChaseCameraMethod();
            base.Play();
        }
    }

    // even more awesome game using flight cameras
    class Game2 : GameBase
    {
        public new FlightCamera Camera { get; set; }
        public Game2()
            : base()
        {
            Camera = new FlightCamera();
        }

        public override void Play()
        {
            Console.WriteLine("Game.Camera is null? {0}", Camera == null);
            Camera.SpecialFlightCameraMethod();
            base.Play();
        }
    }
}

OUTPUT:

Game.Camera is null? False
SpecialChaseCameraMethod
GameBase.Camera is null? False
SpecialCameraBaseMethod

Game.Camera is null? False
SpecialFlightCameraMethod
GameBase.Camera is null? True

Is inheritance even the best approach for this?


回答1:


You need to use override instead of new.

You can still return a Bar object, thought it will still return as BarBase. But override is the only way to make sure that Foo's property is used instead of FooBase's property when the Foo object is cast as FooBase.

Generics could help in this situation, mainly on the FooBase class:

class FooBase<T> where T: BarBase {
    public virtual T Bar { get; set; }
    public FooBase() { }
}

class Foo : FooBase<Bar> {
    public override Bar Bar { get; set; }
    public Foo() : base() {
        Bar = new Bar();
    }
}

And here's the modifications to Main():

static void Main( string[] args ) {
    var foo = new Foo();
    var foobase = foo as FooBase<Bar>;

    Console.WriteLine( "Foo is null? {0}", foo == null );
    Console.WriteLine( "Foo.Bar is null? {0}", foo.Bar == null );
    Console.WriteLine( "FooBase is null? {0}", foobase == null );
    Console.WriteLine( "FooBase.Bar is null? {0}", foobase.Bar == null );
    Console.ReadKey();
}

The constraint of generic parameter T to BarBase means any subclass of FooBase can opt-in to any subclass of BarBase, or just deal with BarBase:

class VagueFoo : FooBase<BarBase> { }



回答2:


I'm going to present an answer to my own question. Actually, two answers. There are likely many other answers but none are finding their way here.

Solution #1: Parallel Hierarchies

This idea was brought up by @Joel B Fant in an earlier comment. I looked over how ADO.NET is implemented (via Reflector) to come up with an implementation. I'm not thrilled about this pattern because it means multiple properties for the same family of objects- but it is a reasonable solution.

(Let's assume that the CameraBase, FlightCamera, and ChaseCamera from my question are still defined to save space)

CODE

// abstract game
abstract class GameBase
{
    public virtual CameraBase CameraBase { get; set; }
    public GameBase() { }
    public virtual void Play()
    {
        Console.WriteLine("GameBase.CameraBase is null? {0}", CameraBase == null);
        if (CameraBase != null)
            CameraBase.SpecialCameraBaseMethod();
    }
}

// awesome game using chase cameras
class Game1 : GameBase
{
    public override CameraBase CameraBase 
    { 
        get { return Camera; } 
        set { Camera = (ChaseCamera)value; } 
    }
    public ChaseCamera Camera { get; set; }
    public Game1()
    {
        Camera = new ChaseCamera();
    }
    public override void Play()
    {
        Console.WriteLine("Game.Camera is null? {0}", Camera == null);
        Camera.SpecialChaseCameraMethod();
        base.Play();
    }
}

Solution #2: Inheritance, Injection, and a Twist

This "pattern" might have a real name, but I don't know it. It's very similar to Parallel Hierarchies (as well as my original example) except I'm injecting the camera through the constructor and using the base properties (with casting) for access. I suppose I could have avoided the injection and do a straight assignment to the base property as well (but why avoid injection? It's so useful!). The twist is simply the required casting for the properties. It's not clean, but it's not ugly either. This is my current preferred solution.

CODE

// must inject the camera for this solution
static void Main(string[] args)
{
    new Game1(new ChaseCamera()).Play();
    Console.ReadLine();
}

// abstract game
abstract class GameBase
{
    public virtual CameraBase Camera { get; set; }
    public GameBase(CameraBase camera) // injection
    {
        Camera = camera;
    }
    public virtual void Play()
    {
        Console.WriteLine("GameBase.Camera is null? {0}", Camera == null);
        if (Camera != null)
            Camera.SpecialCameraBaseMethod();
    }
}

// awesome game using chase cameras
class Game1 : GameBase
{
    public new ChaseCamera Camera 
    { 
        get { return (ChaseCamera)base.Camera; } 
        set { base.Camera = value; } 
    }
    public Game1(ChaseCamera camera) : base(camera) { } // injection
    public override void Play()
    {
        Console.WriteLine("Game.Camera is null? {0}", Camera == null);
        Camera.SpecialChaseCameraMethod();
        base.Play();
    }
}

Both of these solutions give me the desired results with an acceptable implementation.




回答3:


class Foo : FooBase
{
    public override BarBase Bar { get; set; }
    public Foo() : base()
    {
        // populate our modified property with the derived class
        Bar = new Bar();
        //base.Bar = Bar; // I don't want to do this, but how can I avoid it?
    }
}

You may try this: change Property type to BarBase and use override. Then use polimorphism to assign a derived class Bar object to the BarBase Property. Property Bar is virtual so the right object will be returned.




回答4:


Heres a tip for solution:

class GameAndCameraExample
{
    static void Main(string[] args)
    {
        new Game1().Play();
        Console.WriteLine();
        new Game2().Play();
        Console.ReadLine();
    }    

    // abstract camera  
    abstract class CameraBase
    {
        public void SpecialCameraBaseMethod()
        {
            Console.WriteLine("SpecialCameraBaseMethod");
        }
    }    

    // camera implementation    
    class ChaseCamera : CameraBase
    {
        public void SpecialChaseCameraMethod()
        {
            Console.WriteLine("SpecialChaseCameraMethod");
        }
    }    

    // a different camera implementation   
    class FlightCamera : CameraBase
    {
        public void SpecialFlightCameraMethod()
        {
            Console.WriteLine("SpecialFlightCameraMethod");
        }
    }    

    // abstract game   
    abstract class GameBase<T> where T : CameraBase, new()
    {
        public T Camera { get; set; }

        public GameBase()
        {
            Camera = new T();
        }

        public virtual void Play()
        {
            Console.WriteLine("GameBase.Camera is null? {0}", Camera == null); 
            if (Camera != null) // it will be null for Game2 :-(              
                Camera.SpecialCameraBaseMethod();
        }
    }   

    // awesome game using chase cameras  
    class Game1 : GameBase<ChaseCamera>
    {
        public override void Play()
        {
            Console.WriteLine("Game.Camera is null? {0}", Camera == null); 
            Camera.SpecialChaseCameraMethod(); 
            base.Play();
        }
    }

    // even more awesome game using flight cameras    
    class Game2 : GameBase<FlightCamera>
    {
        public override void Play()
        {
            Console.WriteLine("Game.Camera is null? {0}", Camera == null); 
            Camera.SpecialFlightCameraMethod(); 
            base.Play();
        }
    }
}

Hope you find this useful. -Zsolt



来源:https://stackoverflow.com/questions/6239790/new-modifier-causes-base-implementations-to-have-null-property-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!