C# 如何处理C语言中的大量变量#

C# 如何处理C语言中的大量变量#,c#,variables,C#,Variables,我在做一个关于遗传学的游戏,我偶然遇到了一个小问题。(事实上这更像是一个问题) 我有一个DNA类,里面有很多变量,用来存储游戏中每个“生物”的信息 public class DNA { float size; float speed; float drag; float eyeSize; int numberOfEyes; float color_r; [...] } 现在让我们想象一下,我想平均两个DNA 我可以做到: DNA Aver

我在做一个关于遗传学的游戏,我偶然遇到了一个小问题。(事实上这更像是一个问题)

我有一个DNA类,里面有很多变量,用来存储游戏中每个“生物”的信息

public class DNA {
    float size;
    float speed;
    float drag;
    float eyeSize;
    int numberOfEyes;
    float color_r;
    [...]
}
现在让我们想象一下,我想平均两个DNA

我可以做到:

DNA AverageDNAs (DNA dna1, DNA dna2) {
    DNA newDNA = new DNA ();
    newDNA.size = (dna1.size+dna2.size)/2f;
    newDNA.speed = (dna1.speed+dna2.speed)/2f;
    [...]
}
但它看起来很长,每次我要做一些计算,我需要一个一个地重写每个变量

因此,我创建了一个函数,将所有变量(在0和1之间标准化)存储到一个列表中

public class DNA {
    public float size;
    public float speed;
    [...]
    private List<float> tempList;
    public List<float> ToList() {
        if (tempList == null) {
        tempList = new List<float>();
        toReturn.Add (size/sizemax);
        toReturn.Add (speed/speedmax);
        [...]
        }
        return tempList
    }
    public static ADN fromList(List<float> list) {
        ADN toReturn = new ADN();
        toReturn.size = list[0]*sizemax;
        [...]
    }
}
公共类DNA{
公众浮点数;
公众浮标速度;
[...]
私人名单圣殿骑士;
公众名单收费表(){
if(templast==null){
tempList=新列表();
t返回。添加(尺寸/尺寸最大值);
t返回。添加(速度/速度最大值);
[...]
}
回归圣堂武士
}
公共静态ADN fromList(列表){
ADN toReturn=新ADN();
toReturn.size=list[0]*sizemax;
[...]
}
}
现在,我可以做:

DNA AverageDNAs (DNA dna1, DNA dna2) {
    List<float> dnaList1 = dna1.ToList();
    List<float> dnaList2 = dna2.ToList();
    List<float> newDNA = new List<float>();
    for (int i = 0; i<dnaList1.Count; i++) {
        newDNA.Add ((dnaList1[i]+dnaList2[i])/2f);
    }
    return DNA.fromList(newDNA);
}
DNA平均DNA(DNA dna1,DNA dna2){
List dnaList1=dna1.ToList();
List dnaList2=dna2.ToList();
List newDNA=newlist();

对于(int i=0;i如果始终对所有应用相同的操作,则可以使用参数指定要执行的操作:

public DNA Cross(DNA dna1, DNA dna2, Func<float, float, float> operation)
{
  var newDNA = new DNA();

  newDNA.size = operation(dna1.size, dna2.size);
  newDNA.speed = operation(dna1.speed, dna1.speed);
  // And all the rest
}
等等


如果您确实希望更有选择性地应用该操作,还可以扩展
交叉
方法,以获取一个枚举,指定要更新的字段。当然,您可能还希望添加另一个
Func
(或
操作
,具体取决于您对变异参数的适应程度)以任何您想要的方式更新其余部分。

我认为更好的方法是在DNA类上定义您自己的算术运算符。有关如何操作,请参阅此

public class DNA {
    float size;
    float speed;
    float drag;
    float eyeSize;
    int numberOfEyes;
    float color_r;
    [...]

   public static DNA operator +(DNA dna1, dna1 rhs) 
   {
       DNA newDNA = new DNA ();
       newDNA.size = (dna1.size+dna2.size);
       newDNA.speed = (dna1.speed+dna2.speed);
       [...]

       return newDNA;
   }
}
然后你可以做:

var newDna = dna1 + dna2;
我想你可以在内部使用@Luaan suggestion来避免一遍又一遍地重复输入同一个东西,但是使用IMHO的时候会更整洁

 public static DNA operator +(DNA dna1, dna1 rhs) 
 {
      return Cross(dna1, dna2, (d1, d2) => d1 + d2);  
 }

您可以使用反射来获取属性,然后在对象列表中循环使用它们吗

如果您不熟悉反射,下面是一个MSDN示例,应该会有所帮助:

使用字典而不是字段。因此,当您需要获得平均值时,可以使用:

foreach(var item in this._dict)
{
    avgDNA._dict[item.Key] = (item.Value + other._dict[item.Key]) / 2;
}

这样,您就不必关心新字段。

您可以围绕一个
特征定义一个接口/类模型,它知道如何与一个类似的特征“组合”。例如,一个表示为float的trair,其中组合是一个平均值,可以表示为:

public class FloatTrait 
{
    private float val;
    public FloatTrait(float val)
    {
        this.val = val;
    }
    public float Value{get { return this.val; }}

    public FloatTrait CombineWith(FloatTrait t)
    {
       return new FloatTrait((this.Value + t.Value)/2.0f);
    }
}
然后,您可以在
DNA
对象中拥有表示特征的属性,而不是绝对值

public class DNA
{
    public FloatTrait Size{get;set;}
    public FloatTrait Speed{get;set;}
}
添加了一个字典来引用它们,这使得它们的组合更加简单(值得注意的是:我使用字符串文字作为键,使用枚举更合适!h/t@Luaan)

公共类DNA
{
私有字典特性=新字典(){
{“大小”,新的FloatTrait(5.0)},
{“速度”,新的FloatTrait(50.0)},
}
公共DNA(字典目录)
{
this.traits=dict;
}
public FloatTrait this[string key]{get{return traits[key];}
公共浮点大小{get{return traits[“Size”].Value;}
公共浮点速度{get{return traits[“Speed”].Value;}
公共DNA组合(其他DNA)
{
var newDict=this.traits.ToDictionary(k=>k.Key
v=>v.Value.CombineWith(其他[v.Key]);
返回新的DNA(newDict);
}
}   
您可以扩展它以支持除由
浮点表示的那些之外的“特征”。如果平均值不合适,您还可以更改“组合”特征的机制


这个工作的实例:

我在这里假设您并不总是执行相同的操作,并且操作取决于对象的类型

首先要做的是为定义基本功能的
float
添加一个额外的抽象层

public interface IDNAComputable
{
    IDNAComputable Grow(float length);
    // etc.
}
如果最终大多数方法都是“空”的,那么将其作为一个抽象类,并定义一些不起任何作用的基本实现

接下来,添加一个封装float、int、string或任何您喜欢的类。您可能希望像我一样创建语义类,如weight、height等。在本例中,我使用struct,但如果您在其中填充了大量内容,您可能希望使用类。这里的主要问题是,它们都共享一个我们可以用来处理内容的接口:

public struct Height : IDNAComputable
{
    // implement members
}
更新后,您似乎还想剪切内容等。可能需要添加一个额外的枚举来描述属性。因为它们映射到整数,所以您可以使用它们做很多有趣的事情,包括随机索引等

public enum DNAProperty : int
{
    BodyWidth,
    BodyHeight,
    MouthWidth,
    MouthHeight,
    // ...
}
最后一件事是构建DNA本身:

public class DNA
{
    private Width bodyWidth;
    private Height bodyHeight;
    private Width mouthWidth;
    private Height mouthHeight;
    // etc.

    public void Update(Func<IDNAComputable, IDNAComputable, DNAProperty> op)
    {
        bodyHeight = (Height)op(bodyHeight, DNAProperty.BodyHeight);
        bodyWidth = (Width)op(bodyWidth, DNAProperty.BodyWidth);
        // etc for all other stuff.
    }

    public void Merge(Func<IDNAComputable, IDNAComputable, IDNAComputable, DNAProperty> op, DNA otherCreature)
    {
        bodyHeight = (Height)op(bodyHeight, otherCreature.bodyHeight, DNAProperty.BodyHeight);
        bodyWidth = (Width)op(bodyWidth, otherCreature.bodyWidth, DNAProperty.BodyWidth);
        // etc for all other stuff.
    }
}
根据您的评论更新一些实际示例:

现在我正在使用ToList()方法来比较两个DNA(以了解它们之间的差异),对一个随机变量应用一个突变,并交叉两个DNA(我不是平均值,这只是一个示例)

使用这种方法计算差异很容易,即使您的“高度”属性与“宽度”属性不同。如果愿意,您甚至可以引入“身体质量”属性,等等:

double diff = 0;
creature.Merge((lhs, rhs, prop) => { diff += lhs.Difference(rhs); return lhs; });
…或者,如果您只想为此使用一组特定的属性,您也可以这样做:

double diff = 0;
creature.Merge((lhs, rhs, prop) => 
    { 
        if (prop == DNAProperty.BodyMass)
        {
            diff += lhs.Difference(rhs); 
        } 
    return lhs; });
列表非常有用,因为我只需要选择交叉点、迭代列表和“剪切”

不需要列表:

double diff = 0;
creature.Merge((lhs, rhs, prop) => 
    { 
        diff += lhs.Difference(rhs); 

        if (diff > SomeCutThreshold) { return lhs; }
        else { return rhs; }
    });
…或者你想知道切入点在哪里

切割点的意思是:
for(inti=0;icutPoint?dna1[i]:dna2[i]}

总结

虽然这看起来可能需要更多的工作,但如果你做出了正确的抽象和继承树,你应该得到更少的代码,而不是更多
double diff = 0;
creature.Merge((lhs, rhs, prop) => { diff += lhs.Difference(rhs); return lhs; });
double diff = 0;
creature.Merge((lhs, rhs, prop) => 
    { 
        if (prop == DNAProperty.BodyMass)
        {
            diff += lhs.Difference(rhs); 
        } 
    return lhs; });
double diff = 0;
creature.Merge((lhs, rhs, prop) => 
    { 
        diff += lhs.Difference(rhs); 

        if (diff > SomeCutThreshold) { return lhs; }
        else { return rhs; }
    });
double diff = 0;
DNAProperty cutpoint = (DNAProperty)int.MaxValue;
creature.Merge((lhs, rhs, prop) => 
    { 
        diff += lhs.Difference(rhs); 

        if (diff > SomeCutThreshold) { cutpoint = prop; diff = double.MinValue; }
        return lhs;
    });

creature.Merge((lhs, rhs, prop) => ((int)prop > (int)cutpoint)?lhs:rhs);