Почему Delegate.CreateDelegate допускает недопустимые преобразования? - PullRequest
4 голосов
/ 16 октября 2010

Редактировать: Я подал отчет об ошибке в Microsoft Connect :: https://connect.microsoft.com/VisualStudio/feedback/details/614234/delegate-createdelegate-allows-binding-functions-with-enum-parameters-to-the-enum-base-type#details

Рассмотрим следующее:

public static Int32 Test(Int16 @short)
{
    return @short;
}

и из вызывающего кода ::

Func<Int16,Int32> test = Test; //valid method group conversion.
Func<Int16,StringComparison> test2 = Test; // doesn't compile, not valid method group conversion.
Func<Int16,StringComparison> test3 = test; // doesn't compile, invalid contra-variance.
Func<Int16,StringComparison> test4 = Delegate.CreateDelegate(typeof(Func<Int16,StringComparison>),test.Method) as Func<Int16,StringComparison>; // works fine.

Почему Delegate.CreateDelegate может выполнять это странное преобразование, которое не позволяют никакие другие средства создания функции?Что еще хуже, несколько более разумное преобразование, чтобы сказать, Int64 или Object оба не удаются.Я понимаю, что StringComparison «расширяет» Int32, но я подумал, что это было скорее уловкой компилятора, так как enum расширяет класс Enum.

Кроме того, это преобразование также работает для DynamicMethod.CreateDelegate.

Edit только что попробовал с Nullable<Int32>, он не работает, что вызывает недоумение.Я думаю, что единственное «бесплатное» преобразование - это Enum типы в их базовый тип, но почему?

Обратите внимание, что это не позволяет преобразовать метод экземпляра int (например, GetHashCode) в метод open, которыйпринимает тип enum, поэтому я считаю, что это ошибка, поскольку поведение несовместимо.

Редактировать: если мы удалим строки test2 и test3, мы можем затем проверить, чтобы увидеть, что метод, делегат и "«незаконно» делегировать всю работу, как ожидалось.

Console.WriteLine(Test(0)); // prints 0
Console.WriteLine(test(0)); // prints 0
Console.WriteLine(test4(0)); //prints CurrentCulture

Редактировать: Вот очень большое злоупотребление этим, которое я написал примерно за 10 минут.Это создает IEqualityComparer<T> для TEnum, в основном захватывая единицу для ее базового типа, а затем просто оборачивая Equals и HashCode и используя этот трюк / злоупотребление для преобразования параметров в TEnums, а не в базовый тип.Если это ошибка, которую я хотел бы знать, чтобы не пытаться полагаться на это поведение.

class EnumComparer<TEnum> : EqualityComparer<TEnum> where TEnum : struct
{
    static Func<TEnum, TEnum, bool> s_Equals;
    static Func<TEnum, int> s_HashCode;
    static EnumComparer<TEnum> s_default;
    static EnumComparer()
    {
        if (!typeof(TEnum).IsEnum) throw new Exception("Not an enum type");
        Type underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
        object equalityComparer = typeof(EqualityComparer<>).MakeGenericType(new[] { underlyingType }).GetProperty("Default").GetGetMethod().Invoke(null, null);
        s_Equals = Delegate.CreateDelegate(typeof(Func<TEnum, TEnum, bool>), equalityComparer,equalityComparer.GetType().GetMethod("Equals", new[]{underlyingType,underlyingType})) as Func<TEnum,TEnum,bool>;
        s_HashCode = Delegate.CreateDelegate(typeof(Func<TEnum, int>), equalityComparer, equalityComparer.GetType().GetMethod("GetHashCode", new[]{underlyingType})) as Func<TEnum, int>;
        s_default = new EnumComparer<TEnum>();
    }

    public static  new EnumComparer<TEnum> Default
    {
        get
        {
            return s_default;
        }
    }
    public override bool Equals(TEnum x, TEnum y)
    {
        return s_Equals(x, y);
    }

    public override int GetHashCode(TEnum obj)
    {
        return s_HashCode(obj);
    }

    private EnumComparer()
    {
    }
}

1 Ответ

2 голосов
/ 16 октября 2010

Это не ошибка.Всегда можно преобразовать тип целочисленного значения в перечисление, подобное StringComparison.Компилятору обычно требуется приведение, но вы здесь обходите компилятор.И так же, как в C #, нет проверки, чтобы убедиться, что интегральное значение действительно представляет одно из значений перечисления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...