在 C# 中处理具有大量非常相似 类 的多态性

Handling polymorphisms with large amount of very similar classes in C#

我正在用不同的粒子进行模拟。他们都有模拟 行为,都与潜在的其他粒子存储在同一个列表中 不同的子类型,并被系统的其余部分平等对待。因此,我创建了 class 粒子,并尝试了不同的多态性方法。这是我大致想要实现的简化版本,用伪代码编写:

//Movement methods
Randomwalk() {//code}
Diffusion() {//code}
Steered_Motion() {//code}

//Special actions
Pollute() {//code}
Cleanup() {//code}

//Particles
Particle_Type       Movement_Method           Special_Action
Oil                 Diffusion()               Pollute()
Sorbent             Diffusion()               Cleanup()
Packman_random      Randomwalk()              Cleanup()
Packman_steered     Steered_Motion()          Cleanup()
... (50 more rows)

我想到了三种方法来实现这一点,但其中两种只对少量粒子类型(<= 10)有效,而第三种方法是一个充满希望的梦想被 DataTable class 在 C# 中工作。

我尝试的第一种方法是定义一个具有不同粒子类型的枚举,向粒子添加一个字段 class 定义它是哪种粒子,并使用一个使用该字段的 switch 语句来确定调用哪个移动方式,调用哪个特殊动作

public class Particle {
     public enum ParticleType: int {
          Oil = 0,
          Sorbent = 1,
          //...etc
     }
     
     public readonly ParticleType TypeOfParticle;

     Movement() {
          switch(TypeOfParticle) {
               case ParticleType.Oil:
                    Diffusion();
                    break;
               case ParticleType.Sorbent:
                    Diffusion();
                    break;
          }
     }
}

然而,由于某些类型需要额外的字段,这留下了各种字段的混乱,这些字段只被一个子类型使用。此外,枚举类型没有任何内置支持要求唯一性,这意味着如果没有正确检查枚举定义的赋值,两种粒子类型最终可能会在 switch 语句中触发相同的情况。

第二种方法是使用继承。在这里我仍然使用相同的粒子 class,但每个粒子类型都是粒子 class.

的子粒子
public class Particle {
     virtual public void Movement() {}

     //Define methods
     Randomwalk() {//some code}
     Diffusion() {//some code}
     Steered_Motion() {//some code}
}

public class Oil: Particle {
     override public void Movement() {
          Diffusion();
     }
}

public class Sorbent: Particle {
     override public void Movement() {
          Diffusion();
     }
}

虽然这解决了只为某些类型设置字段的问题,但它创建了一个包含大量 class 的非常长的列表。也许只有我一个人,但管理这个类型列表真的感觉像是一件苦差事。因此,我开始在第三种方法中寻找一些东西。

第三种所需的方法是使用 DataTable 创建一个 table,所有不同的粒子类型作为行,移动方法和特殊操作作为列。 运行将使用委托来完成这些方法。不幸的是,数据表是在 运行 时填充的,而我需要在编译时定义 classess。此外,管理 DataTables 听起来也不是最舒服的table任务。

所以我终于来问你要做什么了。有没有我可以使用的类似于 DataTable 的类型?我是一名物理和数学专业的学生,​​编程时间不多,所以即使听起来很基础,我也可能完全错过了。非常感谢任何输入! 提前谢谢你:)

你或许可以用delegate来代表你的动作和特殊动作。或者 Action,Func 也只是委托。取决于你的需要。 在此示例中,我已将 Particle 包含在委托中,因此根据示例,扩散委托的行为可能会有所不同,具体取决于粒子的类型。 然后你只需要一个 ParticleFactory 来根据你想要的 ParticleType 正确实例化你的粒子。

        public delegate void SpecialAction(Particle particle);
        public delegate void Movement(Particle particle);

        public static Movement Diffusion = (particle) => { };
        public static Movement RandomWalk = (particle) => { };
        public static Movement Steered_Motion = (particle) => { };

        public static SpecialAction Pollute = (particle) => { };
        public static SpecialAction Cleanup = (particle) => { };

        public enum ParticleType
        {
            Oil = 0,
            Sorbent = 1,
            Packman_random=2,
            Packman_steered=3,
        }

        public class ParticleFactory
        {
            public static Particle BuildParticle(ParticleType particleType) {
                switch(particleType)
                {
                    case ParticleType.Oil:
                        {
                            return new Particle(particleType, Diffusion, Pollute);
                        }
                    case ParticleType.Sorbent:
                        {
                            return new Particle(particleType, Diffusion, Cleanup);
                        }
                    case ParticleType.Packman_random:
                        {
                            return new Particle(particleType, RandomWalk, Cleanup);
                        }
                }
                return null;
            }
        }

        public class Particle {

            private readonly ParticleType particleType;
            private readonly Movement movement;
            private readonly SpecialAction specialAction;


            internal Particle(ParticleType particleType, Movement movement, SpecialAction specialAction)
            {
                this.particleType = particleType;
                this.movement = movement;
                this.specialAction = specialAction;
            }

            public void Move()
            {
                movement.Invoke(this);
            }

            public void Special()
            {
                specialAction.Invoke(this);
            }
        }

编辑 继承 您始终可以子类化 Particle 以提供一些额外的字段。每个子类都有自己的字段。

public class SubParticle : Particle {
    public int OilParticlesRemoved {get; set;}
}

然后工厂只 return 一个 SubParticle 而不是 Particle。 仍然是如何在代码中访问这些字段......模式匹配可能会有所帮助。

SubParticle some = new SubParticle();  
switch (some)  
{  
  case Particle particle: // Type pattern
    particle.Move();
    break;  
  case SubParticle subParticle:  // Type pattern
    subparticle.OilParticlesRemoved = 10;
    subparticle.Move();
    break;  
}

装饰器 另一种解决方案可能是使用装饰器。在这种情况下,Particle 上的方法应该是虚拟的。 Factory return 一个 Sorbent 装饰器,它是一个 Particle。但是装饰器比基本的 Particle.

存储更多的属性
public Particle {
  ...
  public virtual void Move() {
    movement();
  }
  ...
}

public Sorbent : Particle {
  private readonly Particle particle;
  public Sorbent(Particle particle) {
    this.particle = particle;
  }
  public int OilParticlesRemoved {get; set;}

  public override void Move() {
    OilParticlesRemoved = 10;
    particle.Move();
  }
}

仍然是,如果要从代码中访问这些属性,则必须区分类型。

我认为您的第二种方法是正确的,尤其是在以下情况下:

  • 您要建模的粒子类型数量相对固定,
  • 您正在建模的某些粒子类型具有其他粒子类型所没有的特殊行为,and/or
  • 您想要捕获的粒子类型之间存在任何类型的分组或共性,作为 class 层次结构的一部分
public abstract class Particle
{
    // standard movement behaviors available to any subclass of Particle
    protected void RandomWalk() {...}
    protected void SteeredMotion() {...}
    protected void Diffusion() {...}

    // standard "special actions" available to subclasses of Particle
    protected void Pollute() {...}
    protected void CleanUp() {...}

    // abstract methods expected to call the above methods
    protected abstract void Move();
    protected abstract void SpecialAction();
}

public class OilParticle : Particle
{
    protected override void Move() { Diffusion(); }
    protected override void SpecialAction() { Pollute(); }
}

public class PackmanRandomParticle : Particle
{
    protected override void Move() { RandomWalk(); }
    protected override void SpecialAction() { CleanUp(); }
}

我已经想出了自己的解决方案,并希望与可能遇到与我类似问题的任何人分享。

我创建了一个自定义 class,用作数据table 类条目,确定每个 child class 的所需方法和行为 Particle,并让每个粒子在 table.

中搜索自己的行为
internal class ParticleTable {
     //Fields for devining suptype behaviour
     internal readonly Type SubType;
     internal readonly MotionDelegate MotionMethod;
     internal readonly ActionDelegate ActionMethod;
}

public abstract class Particle {
     //Delegate definitions
     internal delegate void MotionDelegate(Particle self);
     internal delegate void ActionDelegate(Particle self);

     //SubType behaviour definitions
     internal static readonly ImmutableArray<ParticleTable> SubTypes = ImmutableArray.Create(
          new ParticleTable[]{
               new ParticleTable(Particle_Oil, Randomwalk, Pollute);
               new ParticleTable(Particle_Sorbent, Randomwalk, Cleanup);
               //...
          }
     );

     //Update method
     public void ParticleUpdate() {
          //find entry of own type in table
          ParticleTable self_def;
          foreach (ParticleTable sub in SubTypes) {
               if (sub.SubType == this.GetType()) {
                    self_def = sub;
                    break;
               }
          }
          
          //get methods
          MotionDelegate motion = self_def.MotionMethod;
          ActionDelegate action = self_def.ActionMethod;
          
          //execute actions
          motion.Invoke(this);
          action.Invoke(this);
     }
}

//library of subtypes
public class Particle_Oil : Particle {
     //some type specific code
}

public class Particle_Sorbent : Particl {
     //some type specific code
}
//...

这样,通过更改 new ParticleTable() 调用中的参数值,可以在“table”Subtype 中完成任何配置。任何特定于子类型的代码都可以像往常一样在专用 class 中编写。