问题
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