简体   繁体   中英

Avoid casting when accessing base class properties from a derived class

As a noob to C# I'm struggling to wrap my head around the best solution for casting with my scenario, basically I'm trying to avoid casting base class objects from a derived class.

In my example within GridManagerBase class the GridTarget property is defined by the generic definition, but the SelectedTarget is using a base interface, which from with the GridManager class requires me to cast to gain access to additional properties.

So I'm trying to work out a clean solution without the casting.

I've searched many examples online and cant quite get the answer I'm looking for, so looking for any advice really.

Many thanks.

public class GridManager : GridManagerBase<IGridPoint>
{
    void DoWork()
    {
        var result = GridTarget.HasVehicle; // works

        var result2 = ((ITargetObject)SelectedTarget).SomeValue; // works with cast
    }
}

public abstract class GridManagerBase<TGridPoint> : IGridManagerBase<TGridPoint>
       where TGridPoint : class, IGridPointBase
{
    public TGridPoint GridTarget { get; protected set; }

    public ITargetObjectBase SelectedTarget { get; protected set; }

    //public IVehicleObjectBase SelectedVehicle { get; protected set; }
}

public interface IGridManagerBase<TGridPoint>
{
    TGridPoint GridTarget { get; }

    ITargetObjectBase SelectedTarget { get; }
}

public interface IGridPointBase
{
    int Row { get; }
}

public interface IGridPoint : IGridPointBase
{
    bool HasVehicle { get; }
}


public interface ITargetObjectBase
{
    int Row { get; set; }
}

public interface ITargetObject : ITargetObjectBase
{
    int SomeValue { get; }
}

UPDATE Ok, so my primary goal is to have more than one GridManager from which other objects have common functionality, so my thought was to use base interfaces to achieve this.

From the GridManager the SelectedTarget (ITargetObjectBase) object will have common logic, which is fine, but unique logic also at the GridManager.

I did think about going the route suggested by Prasanna prior to me asking the question, should have mentioned that, kinda why I left the IVehicleObjectBase commented in, but I'm not sure what guidelines there are for the creating several generic definitions.

I've added some additional code with concrete classes tho hopefully give a better idea what I'm on about.

public class GridManager : GridManagerBase<IGridPoint>
{
    public GridManager()
    {
        GridTarget = new GridPoint();

        SelectedTarget = new TargetObject(1, 2);
    }

    public void DoWork()
    {
        var result = GridTarget.HasVehicle; // works

        var result2 = ((ITargetObject)SelectedTarget).SomeValue; // works with cast

        SelectedTarget.DoSomeCommonWork();
    }
}

public class GridManagerB : GridManagerBase<IGridPoint>
{
    public GridManagerB()
    {
        GridTarget = new GridPoint();

        SelectedTarget = new TargetObjectB(1, string.Empty);  
    }

    public void DoWork()
    {
        var result = GridTarget.HasVehicle; // works

        var result2 = ((ITargetObjectB)SelectedTarget).SomeOtherValue; // works with cast

        SelectedTarget.DoSomeCommonWork();
    }
}

public abstract class GridManagerBase<TGridPoint> : IGridManagerBase<TGridPoint>
       where TGridPoint : class, IGridPointBase
{
    public TGridPoint GridTarget { get; protected set; }

    public ITargetObjectBase SelectedTarget { get; protected set; }

    //public IVehicleObjectBase SelectedVehicle { get; protected set; }
}

public interface IGridManagerBase<TGridPoint>
{
    TGridPoint GridTarget { get; }

    ITargetObjectBase SelectedTarget { get; }
}

public interface IGridPointBase
{
    int Row { get; }
}

public interface IGridPoint : IGridPointBase
{
    bool HasVehicle { get; }
}


public interface ITargetObjectBase
{
    int Row { get; set; }

    void DoSomeCommonWork();
}

public interface ITargetObject : ITargetObjectBase
{
    int SomeValue { get; }
}

public interface ITargetObjectB : ITargetObjectBase
{
    string SomeOtherValue { get; }
}

public abstract class TargetObjectBase : ITargetObjectBase
{
    public int Row { get; set; }

    public TargetObjectBase(int row)
    {
        Row = row;
    }

    public void DoSomeCommonWork()
    {
        Console.WriteLine(Row.ToString());
    }
}

public class TargetObject : TargetObjectBase, ITargetObject
{
    public int SomeValue { get; private set; }

    public TargetObject(int row, int some)
        : base(row)
    {
        SomeValue = some;
    }
}

public class TargetObjectB : TargetObjectBase, ITargetObjectB
{
    public string SomeOtherValue { get; private set; }

    public TargetObjectB(int row, string other)
        : base(row)
    {
        SomeOtherValue = other;
    }
}

public class GridPoint : IGridPoint
{
    public int Row { get; set; }

    public bool HasVehicle { get; set; }
}

So I think my best two options are either to go the generic definition route or to remove the ITargetObjectBase property from GridManagerBase and just have ITargetObject within an IGridManager interface.

  • The reason that a cast is not needed for GridTarget is that: it is of type IGridPoint which directly contains the definition for HasVehicle .
  • Whereas, SelectedTarget is of type ITargetObjectBase . ITargetObjectBase does not directly contain the definition for SomeValue . So a cast to ITargetObject is needed.
  • But a cast is not required in SelectedTarget.Row as ITargetObjectBase contains definition for Row .

In your case, you could make IGridManagerBase into a type with two generic parameters, as shown below. Then you could call SelectedTarget.SomeValue without a cast

public class GridManager : GridManagerBase<IGridPointA, ITargetObject>
{
    void DoWork()
    {
        var result = GridTarget.HasVehicle; // works
        var result2 = SelectedTarget.SomeValue; // works with cast
    }
}
public abstract class GridManagerBase<TGridPoint, TTargetObject> : IGridManagerBase<TGridPoint,TTargetObject> 
  where TGridPoint : class, IGridPointBase
  where TTargetObject : class, ITargetObjectBase
{
    public TGridPoint GridTarget { get; protected set; }
    public TTargetObject SelectedTarget { get; protected set; }
    //public IVehicleObjectBase SelectedVehicle { get; protected set; }
}
public interface IGridManagerBase<TGridPoint, TTargetObject>
{
    TGridPoint GridTarget { get; }
    TTargetObject SelectedTarget { get; }
}
public interface IGridPointBase
{
    int Row { get; }
}
public interface IGridPoint : IGridPointBase
{
    bool HasVehicle { get; }
}
public interface IGridPointA : IGridPointBase
{
    bool HasVehicle111 { get; }
}
public interface ITargetObjectBase
{
    int Row { get; set; }
}
public interface ITargetObject : ITargetObjectBase
{
    int SomeValue { get; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM