Est-il possible de créer une méthode générique d'énumération au niveau du bit 'IsOptionSet ()'?

StackOverflow https://stackoverflow.com/questions/1614412

Question

Le code ci-dessous facilite la transmission d'un ensemble HtmlParserOptions, puis la comparaison avec une seule option pour voir si elle a été sélectionnée.

[Flags]
public enum HtmlParserOptions
{
    NotifyOpeningTags = 1,
    NotifyClosingTags = 2,
    NotifyText = 4,
    NotifyEmptyText = 8
}

private bool IsOptionSet(HtmlParserOptions options, HtmlParserOptions singleOption)
{
    return (options & singleOption) == singleOption;
}

Ma question est la suivante: est-il possible de créer une version générique (je suppose en implémentant une interface sur les propriétés de la méthode) qui fonctionnera avec toutes les énumérations avec l'attribut Flags?

Était-ce utile?

La solution

Modifier:

La solution la plus simple et la plus simple consiste à effectuer la mise à niveau vers VS2010 Beta2 et à utiliser le .NET 4 Enum.HasFlag . L’équipe du framework a ajouté de nombreux ajouts intéressants à Enum pour les rendre plus agréables à utiliser.

Original (pour le .NET actuel):

Vous pouvez le faire en passant Enum au lieu des génériques:

static class EnumExtensions
{
    private static bool IsSignedTypeCode(TypeCode code)
    {
        switch (code)
        {
            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return false;
            default:
                return true;
        }
    }

    public static bool IsOptionSet(this Enum value, Enum option)
    {
        if (IsSignedTypeCode(value.GetTypeCode()))
        {
            long longVal = Convert.ToInt64(value);
            long longOpt = Convert.ToInt64(option);
            return (longVal & longOpt) == longOpt;
        }
        else
        {
            ulong longVal = Convert.ToUInt64(value);
            ulong longOpt = Convert.ToUInt64(option);
            return (longVal & longOpt) == longOpt;
        }
    }
}

Cela fonctionne parfaitement, comme ceci:

class Program
{
    static void Main(string[] args)
    {
        HtmlParserOptions opt1 = HtmlParserOptions.NotifyText | HtmlParserOptions.NotifyEmptyText;
        Console.WriteLine("Text: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyText));
        Console.WriteLine("OpeningTags: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyOpeningTags));

        Console.ReadKey();
    } 
}

Les impressions ci-dessus:

Text: True
OpeningTags: False

L’inconvénient, c’est que cela ne vous empêche pas de transmettre deux types différents de types Enum à la routine. Vous devez l'utiliser raisonnablement.

Autres conseils

Eh bien, en quelque sorte.

Vous ne pouvez pas ajouter de contrainte pour vous assurer que l'argument de type est un " drapeaux " Enum, et en clair C #, vous ne pouvez pas ajouter de contrainte pour vous assurer qu'il s'agit bien d'une énum en premier lieu ... mais avec un peu de jiggery-pokery, vous pouvez faire en sorte que ce dernier fonctionne. C'est une contrainte valide en IL, mais pas en C #. Il vous faudra alors travailler un peu pour obtenir les options "et". une partie fonctionne de manière générique.

J'ai un projet appelé Melody sans contrainte qui contient des méthodes d'extension utiles pour les énumérations. , via une réécriture IL. Dans ce cas, vous utiliseriez:

if (options.HasAny(optionToTest))

ou

if (options.HasAll(optionToTest))

en fonction de la manière dont vous souhaitez gérer le cas où optionToTest est en réalité plusieurs indicateurs combinés.

Vous pouvez également attendre .NET 4.0: le les modifications apportées à la BCL incluent Enum.HasFlag , qui, je pense, fera ce que vous voulez.

public static bool IsOptionSet<T>(this T flags, T option) where T : struct
{
    if(! flags is int) throw new ArgumentException("Flags must be int");

    int opt = (int)(object)option;
    int fl = (int)(object)flags;
    return (fl & opt) == opt;
}

EDIT: Comme indiqué dans les commentaires, cela ne fonctionnera pas si l’énumération est autre chose qu’un int (qui est la valeur par défaut pour les énumérations). Cela devrait probablement être nommé autrement pour indiquer cela, mais c'est probablement «suffisant» dans la majorité des cas, sauf si vous avez besoin d'un ensemble d'indicateurs avec plus de 31 valeurs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top