在 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 中编写。
我正在用不同的粒子进行模拟。他们都有模拟 行为,都与潜在的其他粒子存储在同一个列表中 不同的子类型,并被系统的其余部分平等对待。因此,我创建了 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 中编写。