【问题标题】:Is there a better way to filter the types of objects that can be passed into an inherited class's constructor?有没有更好的方法来过滤可以传递给继承类的构造函数的对象类型?
【发布时间】:2011-03-15 04:56:54
【问题描述】:

我知道标题有点罗嗦,但我不知道该怎么问这个问题。这基本上是我用来过滤传递给继承类的对象类型的技术。先看代码,我再解释一下……

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

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

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

看到了吗?我不想让你用土豆射击我的车。你只能用彩弹射击它。那么我这样做是否正确?同样,当Prop 类有大约 100 个函数时,这变得更加复杂,我也想过滤到只是彩弹。我不想为Car 类写出那 100 多个函数,对吧?我特别不想为我将要编写的 100 多个 Car 类写出那 100 多个函数。

我在这里说得够清楚吗?

这只是一个例子。我不是在做游戏编程或类似的事情。我只是想给你们一个非常简单的例子来传达我想要的东西。基本上,如果有人试图将土豆传递给 Car 的 Shoot() 函数,我不希望代码编译。

【问题讨论】:

  • 肯定public void Shoot(params IPaintBalls[] paintBalls) : base(paintBalls) 是无效的C#?
  • @Noldorin,您是对的,先生。已修复。

标签: c# inheritance filtering


【解决方案1】:

我可能会做的是让 Prop 接受一个通用类型。即:

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

这些方法随后作为 PaintBalls 公开给 Car 类的用户,即:

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

你也可以有一个非通用的 Prop 类:

public class Prop : Prop<IProjectile>
{
}

您仍然可以使用的地方:

public class Person : Prop
{
}

Person 可以与任何IProjectile 一起拍摄,包括IPotato

【讨论】:

  • 绝对精彩!您的回复非常清晰、简洁和简洁。哦!让我们不要忘记辉煌!这正是我需要的。
【解决方案2】:

如果我理解正确,我会选择泛型:

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

那么你不能只用彩弹来调用 Car.Shoot。

【讨论】:

  • 感谢您指出 Prop 类应该是 abstract。另外,我认为您的意思是,“除了彩弹之外,您不能使用 任何东西 调用 Car.Shoot。”反响很好!
【解决方案3】:

我觉得没问题。重要的是,您使用了接口来抽象彩球的实现。

但是你的代码有点不对劲:

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

更新:在这种情况下,使基类受保护并在派生类上公开一个新方法,但使用基实现。

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

未在编译器中测试,但我相信基本数组 ([]) 是协变的。

更新 2: 或者,定义一个负责拍摄的接口并为其提供 Shoot 方法。

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

更新 3: GenericTypeTea 的响应完成了泛型圈 :-),我目前的解决方案不会阻止你用土豆射击汽车......

【讨论】:

  • 这行不通。您不能使用其他类型覆盖。您将收到“没有合适的方法可以覆盖”的编译器警告。
【解决方案4】:

您不能在 .NET 中纯粹在编译时执行此操作。要实现一个接口,你必须实现它的所有方法。

但显式接口实现将允许您在一定程度上隐藏继承的IProjectile 方法,同时提供公共IPaintBalls 方法。

但是Prop 不会使Shoot 成为虚拟的,所以无论如何你都不能覆盖它。而Prop 是一个类而不是显式实现的接口,不适用。

看来你需要:

  1. 使 Prop.Shoot 虚拟化,并且
  2. 接受运行时检查。

类似这样的:

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

【讨论】:

    【解决方案5】:

    创建更多接口,在本例中为继承 IProjectile 的 IShootableProjectile。 Prop 也一样,这个类太笼统了。

    不要创建可以做很多不同事情的接口或类。将它们分解为涵盖特定职责的小实体。详细了解以下设计原则:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 2016-02-19
      • 2016-01-23
      • 1970-01-01
      • 2019-07-13
      • 1970-01-01
      相关资源
      最近更新 更多