开发者

Is there a better way to filter the types of objects that can be passed into an inherited class's constructor?

开发者 https://www.devze.com 2023-01-07 14:21 出处:网络
I know the title\'s a bit wordy, but I don\'t know how else to ask this question. This is basically the technique that I\'ve been using to filter the types of objects you pass into an inherited class.

I know the title's a bit wordy, but I don't know how else to ask this question. This is basically the technique that I've been using to filter the types of objects you pass into an inherited class. Have a look at the code first and I'll explain more...

public interface IProjectile {}
public interface IPaintBall : IProjectile {}
public interface IPotato : IProjectile {}

public class Prop
{
    public void Shoot(params IProjecti开发者_如何学编程le[] projectiles)
    {
        // logic goes here...
    }
}

public class Car : Prop
{
    public override void Shoot(params IPaintBalls[] paintBalls)
    {
        base.Shoot(paintBalls);
    }
}

See? I don't want you to shoot my car with potatoes. You can only shoot it with paint-balls. So am I doing this the right way? Again, this gets way more complicated when the Prop class has like 100 functions that I also want to filter down to just paint-balls. I don't want to write-out those 100+ functions for the Car class, right? I especially don't want to write-out those 100+ functions for the 100+ Car classes that I'll be writing.

Am I being clear enough here?

This is just an example. I'm not doing game programming or anything like that. I'm just trying to give you guys a really simple example to convey what I want here. Basically, I don't want the code to compile if someone is trying to pass a potato to the Car's Shoot() function.


What I would probably do is to make Prop accept a Generic Type. I.e:

public class Prop<T> where T : IProjectile
{
    public virtual void Shoot(params T[] projectiles)
    {
        // logic goes here... 
    }
}

public class Car : Prop<IPaintBall>
{
    public override void Shoot(params IPaintBall[] projectiles)
    {
        base.Shoot(projectiles);
    }
} 

The methods are then exposed as PaintBalls for users of the Car class, i.e:

Car car = new Car();
car.Shoot(somePaintballs); // Shoot will only take IPaintBall.

You can then also have a non generic Prop class:

public class Prop : Prop<IProjectile>
{
}

Where you can still use:

public class Person : Prop
{
}

Person can be shot with any IProjectile, including IPotato.


If I understand correctly, I would go for generics:

public interface IProjectile { }
public interface IPaintBall : IProjectile { }
public interface IPotato : IProjectile { }

public abstract class Prop<TProjectile> where TProjectile : IProjectile
{
    public void Shoot(params TProjectile[] projectiles)
    {

    }
}

public class Car : Prop<IPaintBall>
{

}

class Program
{
    static void Main(string[] args)
    {
        Car myCar = new Car();

        IPaintBall[] paintballs = PaintBallFactory.GetPaintBalls();

        myCar.Shoot(paintballs);
    }
}

Then you can't call Car.Shoot with nothing but paintballs.


It looks OK to me. Importantly you have used interfaces to abstract the implementation of a paint ball.

However your code is a little off:

public override void Shoot(params IPaintBall[] paintBalls)
{
    base.Shoot(paintBalls);
}

Update: in this case, make the base class protected and expose a new method on the derived class, but use the base implementation.

public class Prop
{
    protected void Shoot(params IProjectile[] projectiles)
    {
        // logic goes here...  
    }
}

public class Car : Prop
{
    public void Shoot(params IPaintBall[] projectiles)
    {
        base.Shoot(projectiles);
    }
}  

Not tested in a compiler, but I believe basic arrays ([]) are co-variant.

Update 2: Alternatively, define an interface responsible for shooting and give it the Shoot method.

public interface IProjectile { }
    public interface IPaintBall : IProjectile { }
    public interface IPotato : IProjectile { }
    public interface IShoot<T> where T : IProjectile
    {
        void Shoot(params T[] projectiles);
    }

    public interface IShootPaintBalls : IShoot<IPaintBall> { }

    public class Prop : IShoot<IProjectile>
    {
        public void  Shoot(params IProjectile[] projectiles)
        {
          // logic
        }
    }

    public class Car : Prop, IShootPaintBalls
    {
        public void  Shoot(params IPaintBall[] projectiles)
        {
            base.Shoot(projectiles);
        }
    }

Update 3: GenericTypeTea's response completes the generics circle :-), my current solution doesn't stop you shooting a car with a potato...


You cannot do this purely at compile time in .NET. To implement an interface you have to implement all its methods.

But explicit interface implementation will allow you to hide, to a degree, the inherited IProjectile method while providing a public IPaintBalls methods.

But there Prop doesn't make Shoot virtual, so you cannot override it anyway. And Prop is a class not an interface to explicit implementation doesn't apply.

It seems you will need to:

  1. Make Prop.Shoot virtual, and
  2. Accept the runtime check.

Something like this:

public class Prop {
  public virtual void Shoot(params IProjectile[] projectiles) {
    ...
  }
}

public class Car : Prop {
  public override void Shoot(params IProjectile[] projectiles) {
    foreach (var p in projectiles) {
      if (!(p is IPaintBall)) {
        throw new ArgumentException("Only shoot cars with paint balls");
      }
    }
    Shoot(projectiles.Cast<IPaintBall>().ToArray());
  }

  public virtual void Shoot(params IPaintBall[] balls) {
    // Allow callers that are calling with paint balls (know at compile time)
    // to come in directly.
  }
}


Create more interfaces, in this case a IShootableProjectile which inherits IProjectile. Same goes for Prop, it's a too general class.

Do not create interfaces or classes that can do a lot of different things. Break them down to small entites covering a specific responsibility. Read more about the following design principles:

http://blog.gauffin.org/2010/06/hello-world/

0

精彩评论

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

关注公众号