【问题标题】:How to add conditional inside Linq Aggregate in C#?如何在 C# 中的 Linq Aggregate 中添加条件?
【发布时间】:2020-01-08 11:43:51
【问题描述】:

我正在尝试使用命令式风格的 Linq,但我无法将 Aggregate 中的这个条件转换为 Linq。

考虑以下两个示例。

简单示例:

public enum Color {
    Red,  // Red score = 10
    Yellow,  // Yellow score = 5
    Green,  // Green score = 2
}

//Populate our sample list
public List<Color> Colors = new List<Color> {Red, Green, Green, Yellow};

//I need help on this one
public float Score => Colors.Aggregate(0.0f, (total, next) => 
{
    //How to properly use conditional inside Aggregate?
    if (next == Color.Red) {
        return total + 10.0f;
    } else if (next == Color.Yellow) {
        return total + 5.0f;
    } else if (next == Color.Green) {
        return total + 2.0f;
    }
    //edit: forgot the default
    return total;
}



Log(Score); //19

编辑:我尝试将条件移动到 Select,但它只会移动问题,这是如何在 Linq Select 中添加条件?

public float Score => Colors.Select(x => 
{
    // The problem still happening
    if (x == Color.Red) {
        return 10.0f;
    } else if (x == Color.Yellow) {
        return 5.0f;
    } else if (x == Color.Green) {
        return 2.0f;
    }
    return 0.0f;
}
.Aggregate(0.0f, (total, next) => total + next);

这里是复杂的例子,基本上它只是一个游戏的属性修饰符,

// This is a Game Status Modifier, for example: "Strength 30 + 10%"
public enum StatModType
{
    Flat = 100, // Flat addition to Stat
    PercentAdd = 200, // Percent addition to Stat
    ... // many other type of addition
}

private float _baseValue = 30.0f;

public List<StatModifier> StatModifiers = new List<StatModifier>
{...} //dummy data

public float Value => StatModifiers.Aggregate(_baseValue, (finalValue, mod) =>
{
    //I need help on this one
    if (mod.Type == StatModType.Flat)
        return finalValue + mod.Value;
    else if (mod.Type == StatModType.PercentAdd)
    // When we encounter a "PercentAdd" modifier
        return finalValue + finalValue * mod.Value;
    else if (mod.Type == ...)
        //and continues below everytime I add more modifier types..
}


Log(Value); // Strength = 33;

编辑:我将发布(信用:https://forum.unity.com/threads/tutorial-character-stats-aka-attributes-system.504095/)命令式代码以防有人需要它,我也很难阅读这个:

private float CalculateFinalValue()
{
    float finalValue = BaseValue;
    float sumPercentAdd = 0; // This will hold the sum of our "PercentAdd" modifiers

    for (int i = 0; i < statModifiers.Count; i++)
    {
        StatModifier mod = statModifiers[i];

        if (mod.Type == StatModType.Flat)
        {
            finalValue += mod.Value;
        }
        else if (mod.Type == StatModType.PercentAdd) // When we encounter a "PercentAdd" modifier
        {
            sumPercentAdd += mod.Value; // Start adding together all modifiers of this type

            // If we're at the end of the list OR the next modifer isn't of this type
            if (i + 1 >= statModifiers.Count || statModifiers[i + 1].Type != StatModType.PercentAdd)
            {
                finalValue *= 1 + sumPercentAdd; // Multiply the sum with the "finalValue", like we do for "PercentMult" modifiers
                sumPercentAdd = 0; // Reset the sum back to 0
            }
        }
        else if (mod.Type == StatModType.PercentMult) // Percent renamed to PercentMult
        {
            finalValue *= 1 + mod.Value;
        }
    }

    return (float)Math.Round(finalValue, 4);
}

如何在 Aggregate / Reduce / Scan 函数中添加条件?

【问题讨论】:

  • 过滤的方法是Where,而不是Aggregate。如果要生成不同的值,即项目,请使用Select。 Aggregate 用于聚合数据,而不是做其他操作员的工作
  • 聚合为所有记录生成一个值,它不能跳过或过滤它们。对于它不喜欢的任何值,它必须返回未修改的累加器值。
  • 您好,我应该在使用Aggregate 之前将if..else 问题移至Select 吗?将提出另一个问题,如何在Select 中添加条件。我不能使用Where,因为需要所有数据。
  • 如果可以的话。但是,如果您需要对不同的记录应用不同的操作,a) 您有一个很奇怪的实现,容易受到订单更改的影响;b) 您应该返回累加器,即finalValue,未修改的return finalValue;。切换Flat和PercentAdd的顺序会得到不同的结果
  • 你只需要默认返回不变的累加器。 (最后一个 else 情况)

标签: c# list linq oop


【解决方案1】:

我建议在这两种情况下提取模型,即

简单示例:

private static Dictionary<Color, float> s_ColorScores = new Dictionary<Color, float>() {
  {Color.Red,   10.0f},
  {Color.Yellow, 5.0f},
  {Color.Green,  2.0f},
};

...

float Score = Colors
  .Sum(color => s_ColorScores[color]);

复杂示例:

private static Dictionary<StatModType, Func<float, float, float>> s_Modifications = new 
  Dictionary<StatModType, Func<float, float, float>> {
    {StatModType.Flat,       (prior, value) => prior + value},
    {StatModType.PercentAdd, (prior, value) => prior + prior * value},
     //TODO: add modification rules here
  };

public float Value => StatModifiers
  .Aggregate(_baseValue, (prior, mod) => s_Modifications[mod.Type](prior, mod.Value));

所以您将拥有游戏的模型(s_ColorScores、s_Modifications...),其中包含规则、设置、平衡等(您可能想要调整,可能是Color.Yellow 得分6.0f 是更好的选择)与简单的业务逻辑分开。

【讨论】:

  • 哦,很好地使用 Sum ,谢谢我学到了更多关于 Linq 的知识,我想用一种可以在 Select 中调用的方法使每个枚举都有自己的类,但这会过于复杂事物。像这样的字典可能会有所帮助
【解决方案2】:

假设与枚举类型相关的行为是静态的而不是动态的,根据this MSDocs 文章,另一种方法是使用枚举类而不是枚举类型。为了简化这一点,您可以使用SmartEnum package。

使用这个库和方法,您的用例变成:

简单示例:

public sealed class Color: SmartEnum<Color>
{
    public static readonly Color Red = new Color (nameof(Red), 1, 10.0f);
    public static readonly Color Yellow = new Color (nameof(Yellow), 2, 20.0f);
    public static readonly Color Green = new Color (nameof(Green), 3, 30.0f);

    private Color(string name, int value, double score) 
      : base(name, value)
    {
      this.Score = score;
    }

    public float Score {get;}
}

float TotalScore = Colors
  .Sum(color => color.Score);

复杂示例:

public sealed class StatMod: SmartEnum<StatMod>
{
    public static readonly StatMod FlatAdd = new StatMod(nameof(FlatAdd), 200, (prev, val)=>prev+val);
    public static readonly StatMod PercentAdd = new StatMod(nameof(PercentAdd), 300, (prev,val)=>prior + prior * value);

    private StatMod(string name, int value, Func<float, float, float> reduce) : base(name, value)
    {
      this.Reduce = reduce;
    }

    public Func<float, float, float> Reduce {get;}
}

public float Value => StatModifiers
  .Aggregate(_baseValue, (prior, mod) => mod.Reduce(prev, mod.Value));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 2021-08-13
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    相关资源
    最近更新 更多