关于C#:为随机枚举值分配属性

Assigning a random enum value a property

大家好,我有一个任务要做。我需要将一个随机的enum分配给一个属性。我的密码是这样的。

1
2
3
4
public enum PegColour
{
    Red, Green, Blue, Yellow, Black, White
}

以及其他类似的班级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class PegContainer
{
   /// <summary>
   /// Dfines the colour of the first peg
   /// </summary>
    public Peg Colour1 { get; set; }

    /// <summary>
    /// Dfines the colour of the secod peg
    /// </summary>
    public Peg Colour2 { get; set; }

    /// <summary>
    /// Dfines the colour of the third peg
    /// </summary>
    public Peg Colour3 { get; set; }

    /// <summary>
    /// Dfines the colour of the forth peg
    /// </summary>
    public Peg Colour4 { get; set; }

    public void GeneratePegs()
    {

    }
}

我的GeneratePegs()方法每次被调用时,都应该将enum颜色中的一种随机分配给属性之一(Colour1Colour2等),以使问题复杂化,我需要随机化器忽略BlackWhite


枚举只是整数,因此整数可以强制转换为枚举。我会这样做:

1
2
3
4
5
6
7
8
9
10
11
12
Random rnd = new Random();

public enum PegColour
{
    Red, Green, Blue, Yellow, Black, White
}

private PegColour GetRandomColoredPeg()
{
    PegColour color = (PegColour)rnd.Next(0, Enum.GetNames(typeof(PegColour)).Length - 2);
    return color;
}

黑白将永远不会被选中,因为它只从前4种颜色中随机选取。只要在黑白挂钩之前添加挂钩,即使在枚举中添加或删除挂钩,此代码也应该每次都有效。所以,如果你想增加新的颜色,你只需要把PegColour改成这样:

1
2
3
4
public enum PegColour
{
    Red, Green, Blue, Yellow, Purple, Orange, Pink, Black, White
}

你不需要改变任何其他东西!

所以你的GeneratePegs()方法应该是这样的:

1
2
3
4
5
6
7
public void GeneratePegs()
{
    Colour1 = GetRandomColoredPeg();
    Colour2 = GetRandomColoredPeg();
    Colour3 = GetRandomColoredPeg();
    Colour4 = GetRandomColoredPeg();
}


一个简单的解决方案是创建一个包含所有符合条件的值的数组:

1
PegColour[] eligibleValues = new[] { PegColour.Red, PegColour.Blue, PegColour.Green, PegColour.Yellow };

然后,您可以使用EDOCX1的一个实例(0)随机选择该数组中的一个索引:

1
var myRandomColour = eligibleValues[myRandom.Next(eligibleValues.Length)];

这样做的一个好处是,您不必为enum常量指定任何特定的数值,以便随机选择工作。这样,如果需要的话,您仍然可以自由地定义和使用数值。

现在,如果PegColour经常被新元素扩展,这可能还是有点不方便。在这种情况下,可以在初始化时从Enum.GetValues方法中检索一次当前定义的常量的完整列表(请注意,此代码段假定已导入System.Linq命名空间以允许从System.Linq.Enumerable访问扩展方法):

1
PegColour[] eligibleValues = Enum.GetValues(typeof(PegColour)).Cast<PegColour>().ToArray();

显然,这还不能满足排除某些颜色的要求。因此,可以在数组创建表达式中直接硬编码此限制:

1
PegColour[] eligibleValues = Enum.GetValues(typeof(PegColour)).Cast<PegColour>().Where(pc => (pc != PegColour.Black) && (pc != PegColour.White)).ToArray();

…或者将要排除的颜色存储在一些集合中,以使事物更具可扩展性:

1
PegColour[] eligibleValues = Enum.GetValues(typeof(PegColour)).Cast<PegColour>().Where(pc => !myExcludedColours.Contains(pc)).ToArray();

请注意,始终可以放置此代码,以便只初始化一次eligibleValues,而不是每次随机检索值。这样就避免了对GetValues进行不必要的、可能代价高昂的调用,也避免了不必要地一直重新生成数组。


我建议您创建一个列表:

1
var pegs = new List<Peg> { Peg.Red, Peg.Green, Peg.Blue, Peg.Yellow };

然后使用这个问题的公认答案,随机排序。最后,将值赋给PEG:

1
2
3
4
Colour1 = pages[0];
Colour2 = pages[1];
Colour3 = pages[2];
Colour4 = pages[3];

我为生成enum类型的随机值的Random对象做了一个非常通用的扩展方法。这适用于各种混乱的枚举,包括那些有可怕的编号方案的枚举。这是通过使用Enum.GetValues获得枚举的所有可能值来实现的。然后随机选择一个随机的可能值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static TEnum GetRandomEnum<TEnum>(this Random rand, IEnumerable<TEnum> excludedValues)
{
    var type = typeof(TEnum);
    if (!type.IsEnum)
        throw new ArgumentException("Not an enum type");


    var values = Enum.GetValues(type).Cast<TEnum>();

    if (excludedValues != null && excludedValues.Any())
        values = values.Except(excludedValues);
    //if you call this a lot, you could consider saving this collection in memory
    //   and separate the logic to avoid having to re-generate the collection


    //create a random index for each possible Enum value
    //will never be out of bounds because it NextDouble returns a value
    //between 0 (inclusive) and 1 (exclusive)  or [0, 1)
    int randomIndex = (int) (rand.NextDouble() * values.Count());
    return values.ElementAt(randomIndex);
}

此扩展方法的调用方式如下:

1
2
3
4
5
var randomColor = rand.GetRandomEnum<ColorType>(new List<Colors>
{
    Colors.White,
    Colors.Black
});

演示。