Преобразовать целое число в ящик в целое число с нулевым числом с потенциально другим типом

Учитывая целое число в штучной упаковке (с коротким кодом short, int, ...) Я хочу преобразовать его в unboxed, с нулевым типом. Должно быть возможно преобразование несоответствующих типов (например: boxed int в short?) Прокомментируйте:

using System;

public class Test
{
    public static void Main()
    {
        int? x = Unbox<int>((short)42);
        short? y = Unbox<short>(42);
    }

    static T? Unbox<T>(object x) where T : struct
    {
        return typeof(T) == x.GetType() ? (T)x : (T)Convert.ChangeType(x, typeof(T));
    }
}
11 голосов | спросил Micha Wiedenmann 10 Jpm1000000pmTue, 10 Jan 2017 16:24:29 +030017 2017, 16:24:29

2 ответа


2

Если вы действительно хотите использовать unbox значение, а не просто его преобразовать, вы можете использовать Expression API для создания функции, которая делает именно это, в псевдокоде:

Func<object, TResult> = boxedValue => (TResult)(typeof(boxedValue))boxedValue;

Чтобы построить функцию, вам понадобится вспомогательный класс для создания делегата с правильными типами:

static class UnboxerCache<TResult>
{
    private static readonly ConcurrentDictionary<Type, Func<object, TResult>> UnboxerLookup = new ConcurrentDictionary<Type, Func<object, TResult>>();

    public static Func<object, TResult> GetUnboxer(Type actualType)
    {
        return UnboxerLookup.GetOrAdd(actualType, CreateUnboxer);
    }

    private static Func<object, TResult> CreateUnboxer(Type actualType)
    {
        var boxedParameter = Expression.Parameter(typeof(object), "boxedValue");
        var unboxedValue = Expression.Unbox(boxedParameter, actualType);
        var convertedValue = Expression.Convert(unboxedValue, typeof(TResult));

        var unboxer = Expression.Lambda<Func<object, TResult>>(convertedValue, boxedParameter);

        return unboxer.Compile();
    }
}

Здесь мы имеем сопоставление фактического типа объекта с делегатом, который может распаковать и передать его в тип результата. Построение делегата довольно просто, преобразуйте boxedValue в фактический тип, а затем в тип результата.

Я бы сделал фактический unboxer отдельным классом с этой единственной возможностью:

public static class Unbox
{
    public static TResult? To<TResult>(object boxedValue)
        where TResult : struct
    {
        if (boxedValue == null)
        {
            return null;
        }

        var actualType = boxedValue.GetType();
        var resultType = typeof(TResult);
        if (actualType == resultType)
        {
            return (TResult)boxedValue;
        }

        var unboxer = UnboxerCache<TResult>.GetUnboxer(actualType);
        return unboxer(boxedValue);
    }

    private static class UnboxerCache<TResult>
    {
        private static readonly ConcurrentDictionary<Type, Func<object, TResult>> UnboxerLookup = new ConcurrentDictionary<Type, Func<object, TResult>>();

        public static Func<object, TResult> GetUnboxer(Type actualType)
        {
            return UnboxerLookup.GetOrAdd(actualType, CreateUnboxer);
        }

        private static Func<object, TResult> CreateUnboxer(Type actualType)
        {
            var boxedParameter = Expression.Parameter(typeof(object), "boxedValue");
            var unboxedValue = Expression.Unbox(boxedParameter, actualType);
            var convertedValue = Expression.Convert(unboxedValue, typeof(TResult));

            var unboxer = Expression.Lambda<Func<object, TResult>>(convertedValue, boxedParameter);

            return unboxer.Compile();
        }
    }
}

Наконец, вам следует рассмотреть возможность проверки того, что тип вложенного значения является типом значения (boxedValue.GetType().IsValueType) и, возможно, вызывает ошибку, если это это не так.

ответил Johnbot 11 Jpm1000000pmWed, 11 Jan 2017 13:25:00 +030017 2017, 13:25:00
0

Unbox<T> отсутствует null проверить. Вызов x.GetType() не будет выполнен, если x = null.

static T? Unbox<T>(object x) where T : struct
{
    if (x == null)
        return null;

    return typeof(T) == x.GetType() ? (T)x : (T)Convert.ChangeType(x, typeof(T));
}
ответил Micha Wiedenmann 10 Jpm1000000pmTue, 10 Jan 2017 17:59:27 +030017 2017, 17:59:27

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132