Titus
Titus

Reputation: 178

C# combining single value enum with flags enum

I would like to use a flags enum as a filterset to filter on a single value enum.

When I use one flags enum to provide both, I have two objections:

An example stating my intention (but which does not run properly due to type incompatibility between the two enums)

    enum Material
    {
        wood,
        metal,
        plastic
    }

    [Flags]
    enum Materials
    {
        none = 0,
        wood = 1,
        metal = 2,
        plastic = 4,
        all = 7
    }

    var filter = Materials.all;

    foreach (article in articles.Where(a => Filter.HasFlag(a.FinishMaterial))
        ...

As mentioned, this example does not do what I want. It feels not OK to only use the Materials enum because none and all are not valid values for an article finish.

I could use some casting to int to translate between both enums. But there should be a more elegant, non cluttered way to do this, right?

[edit after answer from Dennis] I actually did think about using a collection as a filter. Also it feels heavy weight, requires more code, while using enums is lightweight. However, when is doesn't do the job I have no user for it, lightweigt/elegant or not...

[edit after marking as ansered] Thank you all for your input!

Upvotes: 0

Views: 849

Answers (3)

Andrei Mihalciuc
Andrei Mihalciuc

Reputation: 2258

The most optimal way is to use int conversions rather than Enum.Parse. To resolve your problem with HasFlag you can create an extension method. Below is the code with test

[TestClass]
public class MaterialsTests
{
    [TestMethod]
    public void Contains_wroks_as_expected()
    {
        var filter = Materials.all;
        Assert.IsTrue(filter.Contains(Material.metal));
    }
}

public static class Extensions
{
    public static bool Contains(this Materials filter, Material material)
    {
        var valueToFilter = (Materials)(int)(material);
        return filter.HasFlag(valueToFilter);
    }
}

public enum Material
{
    wood = 1,
    metal = 2,
    plastic = 4
}

[Flags]
public enum Materials
{
    none = 0,
    wood = 1,
    metal = 2,
    plastic = 4,
    all = 7
}

Upvotes: 1

Evgeniy Mironov
Evgeniy Mironov

Reputation: 787

You have a 2 solutions:

First:

Material value = (Material)Enum.Parse(typeof (Material), Materials.wood.ToString());

I.e. convert enums

Second: use only Materials enum. Not clear but simple.

Upvotes: 0

Dennis
Dennis

Reputation: 37800

IMO, using collection to hold the filter is much easier and readable, than manipulating flags:

// This is equivalent of "var filter = Materials.all";
// an empty collection is equivalent of "Materials.None"
var filter = (Material[])Enum.GetValues(typeof(Material));
// use next line, if you need to modify the filter:
// var filter = ((Material[])Enum.GetValues(typeof(Material))).ToList();

foreach (article in articles.Where(a => filter.Contains(a.FinishMaterial)))
{
    // ...
}

Upvotes: 1

Related Questions