Существует ли обходное решение для универсального ограничения типа “специального класса” Перечисление в C# 3.0? [дубликат]

В Подрывной деятельности (и CVS), репозиторий прежде всего. В мерзавце и подвижный нет действительно понятия репозитория таким же образом; здесь изменения являются центральной темой.

я не думал очень о том, как Вы реализовали бы любого, но мое впечатление (на основе горького опыта и большого чтения) - то, что это различие - то, что делает слияние и ветвление настолько легче в находящихся в нерепозитории системах.

10
задан Blixt 11 September 2009 в 07:02
поделиться

3 ответа

РЕДАКТИРОВАТЬ: Теперь доступна библиотека, поддерживающая это через ildasm / ilasm: UnconstrainedMelody .


Члены команды C # ранее заявляли, что хотели бы иметь возможность поддерживать где T: Enum и , где T: Delegate , но это никогда не было достаточно высоким приоритетом. (Я не уверен, в чем причина наличия ограничения, по общему признанию ...)

Наиболее практичный обходной путь в C #:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    // ...
}

Это теряет проверку во время компиляции для "enum-ness" "но проверяет, что вы используете один и тот же тип в обоих местах. Конечно, у него также есть штраф времени выполнения проверки. Вы можете избежать этого штрафа во время выполнения после первого вызова, используя общий вложенный тип для реализации, которая генерирует исключение в статическом конструкторе:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    return EnumHelper<T>.HasFlags(value, flags);
}

private class EnumHelper<T> where T : struct
{
    static EnumHelper()
    {
        if (!typeof(Enum).IsAssignableFrom(typeof(T))
        {
            throw new InvalidOperationException(); // Or something similar
        }
    }

    internal static HasFlags(T value, T flags)
    {
        ...
    }
}

Как упоминает Греко, вы можете написать метод на C ++ / CLI, а затем ссылаться на библиотека классов из C # в качестве другого варианта.

Я не уверен, в чем причина наличия ограничения, по общему признанию ...)

Наиболее практичный обходной путь в C #:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    // ...
}

Это теряет проверку времени компиляции для "перечисления", но сохраняет проверка того, что вы используете один и тот же тип в обоих местах. Конечно, у него также есть штраф времени выполнения проверки. Вы можете избежать этого штрафа во время выполнения после первого вызова, используя общий вложенный тип для реализации, которая генерирует исключение в статическом конструкторе:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    return EnumHelper<T>.HasFlags(value, flags);
}

private class EnumHelper<T> where T : struct
{
    static EnumHelper()
    {
        if (!typeof(Enum).IsAssignableFrom(typeof(T))
        {
            throw new InvalidOperationException(); // Or something similar
        }
    }

    internal static HasFlags(T value, T flags)
    {
        ...
    }
}

Как упоминает Греко, вы можете написать метод на C ++ / CLI, а затем ссылаться на библиотека классов из C # в качестве другого варианта.

Я не уверен, в чем причина наличия ограничения, по общему признанию ...)

Наиболее практичный обходной путь в C #:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    // ...
}

Это теряет проверку времени компиляции для "перечисления", но сохраняет проверка того, что вы используете один и тот же тип в обоих местах. Конечно, у него также есть штраф времени выполнения проверки. Вы можете избежать этого штрафа во время выполнения после первого вызова, используя общий вложенный тип для реализации, которая генерирует исключение в статическом конструкторе:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    return EnumHelper<T>.HasFlags(value, flags);
}

private class EnumHelper<T> where T : struct
{
    static EnumHelper()
    {
        if (!typeof(Enum).IsAssignableFrom(typeof(T))
        {
            throw new InvalidOperationException(); // Or something similar
        }
    }

    internal static HasFlags(T value, T flags)
    {
        ...
    }
}

Как упоминает Греко, вы можете написать метод на C ++ / CLI, а затем ссылаться на библиотека классов из C # в качестве другого варианта.

но проверяет, что вы используете один и тот же тип в обоих местах. Конечно, у него также есть штраф времени выполнения проверки. Вы можете избежать этого штрафа во время выполнения после первого вызова, используя общий вложенный тип для реализации, которая генерирует исключение в статическом конструкторе:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    return EnumHelper<T>.HasFlags(value, flags);
}

private class EnumHelper<T> where T : struct
{
    static EnumHelper()
    {
        if (!typeof(Enum).IsAssignableFrom(typeof(T))
        {
            throw new InvalidOperationException(); // Or something similar
        }
    }

    internal static HasFlags(T value, T flags)
    {
        ...
    }
}

Как упоминает Греко, вы можете написать метод на C ++ / CLI, а затем ссылаться на библиотека классов из C # в качестве другого варианта.

но проверяет, что вы используете один и тот же тип в обоих местах. Конечно, у него также есть штраф времени выполнения проверки. Вы можете избежать этого штрафа во время выполнения после первого вызова, используя общий вложенный тип для реализации, которая генерирует исключение в статическом конструкторе:

public static bool HasFlags<T>(this T value, T flags) where T : struct
{
    if (!(value is Enum))
    {
        throw new ArgumentException();
    }
    return EnumHelper<T>.HasFlags(value, flags);
}

private class EnumHelper<T> where T : struct
{
    static EnumHelper()
    {
        if (!typeof(Enum).IsAssignableFrom(typeof(T))
        {
            throw new InvalidOperationException(); // Or something similar
        }
    }

    internal static HasFlags(T value, T flags)
    {
        ...
    }
}

Как упоминает Греко, вы можете написать метод на C ++ / CLI, а затем ссылаться на библиотека классов из C # в качестве другого варианта.

10
ответ дан 3 December 2019 в 22:01
поделиться

На самом деле, возможно, с уродливой уловкой. Однако его нельзя использовать для методов расширения.

public abstract class Enums<Temp> where Temp : class {
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
        return (TEnum)Enum.Parse(typeof(TEnum), name); 
    }
}
public abstract class Enums : Enums<Enum> { }

Enums.Parse<DateTimeKind>("Local")

При желании вы можете предоставить Enums частный конструктор и открытый вложенный абстрактный унаследованный класс с Temp в качестве Enum , чтобы предотвратить унаследованные версии для не перечислений.

3
ответ дан 3 December 2019 в 22:01
поделиться

Я не мог устоять перед попыткой обхода C ++, и, поскольку у меня все заработало, я решил поделиться им с остальными!

Вот код C ++ (мой C ++ очень ржавый, поэтому укажите на любые ошибки, в частности, в том, как определены аргументы):

#include "stdafx.h"

using namespace System;
using namespace System::Runtime::CompilerServices;

namespace Blixt
{
namespace Utilities
{
    [Extension]
    public ref class EnumUtility abstract sealed
    {
    public:
        generic <typename T> where T : value class, Enum
        [Extension]
        static bool HasFlags(T value, T flags)
        {
            __int64 mask = Convert::ToInt64(flags);
            return (Convert::ToInt64(value) & mask) == mask;
        }
    };
}
}

И код C # для тестирования (консольное приложение):

using System;
using Blixt.Utilities;

namespace Blixt.Playground
{
    [Flags]
    public enum Colors : byte
    {
        Black = 0,
        Red = 1,
        Green = 2,
        Blue = 4
    }

    [Flags]
    public enum Tastes : byte
    {
        Nothing = 0,
        Sour = 1,
        Sweet = 2,
        Bitter = 4,
        Salty = 8
    }

    class Program
    {
        static void Main(string[] args)
        {
            Colors c = Colors.Blue | Colors.Red;
            Console.WriteLine("Green and blue? {0}", c.HasFlags(Colors.Green | Colors.Red));
            Console.WriteLine("Blue?           {0}", c.HasFlags(Colors.Blue));
            Console.WriteLine("Green?          {0}", c.HasFlags(Colors.Green));
            Console.WriteLine("Red and blue?   {0}", c.HasFlags(Colors.Red | Colors.Blue));

            // Compilation error:
            //Console.WriteLine("Sour?           {0}", c.HasFlags(Tastes.Sour));

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey(true);
        }
    }
}
2
ответ дан 3 December 2019 в 22:01
поделиться