Как проверить, соответствует ли тип неуправляемому ограничению в C #? - PullRequest
0 голосов
/ 29 декабря 2018

Как проверить, соответствует ли тип T ограничению типа unmanaged, чтобы его можно было использовать в контексте, подобном следующему: class Foo<T> where T : unmanaged?Моей первой идеей было typeof(T).IsUnmanaged или что-то подобное, но это не свойство / поле Type класса

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Я не уверен, что что-то подобное уже существует, но вы могли бы реализовать свой собственный метод расширения, подобный следующему:

public static bool IsUnmanaged(this Type type)
{
    // primitive, pointer or enum -> true
    if (type.IsPrimitive || type.IsPointer || type.IsEnum)
        return true;

    // not a struct -> false
    if (!type.IsValueType)
        return false;

    // otherwise check recursively
    return type
        .GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
        .All(f => IsUnmanaged(f.FieldType));
}

(обновление) Для полноты, поскольку рекурсия будет медленной для структур со многими вложеннымичлены, функция может быть сделана быстрее, кэшируя результаты:

private static readonly ConcurrentDictionary<Type, bool> _memoized = 
    new ConcurrentDictionary<Type, bool>();

public static bool IsUnmanaged(this Type type)
{
    bool answer;

    // check if we already know the answer
    if (!_memoized.TryGetValue(type, out answer))
    {

        if (!type.IsValueType)
        {
            // not a struct -> false
            answer = false;
        }
        else if (type.IsPrimitive || type.IsPointer || type.IsEnum)
        {
            // primitive, pointer or enum -> true
            answer = true;
        }
        else
        {
            // otherwise check recursively
            answer = type
                .GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
                .All(f => IsUnmanaged(f.FieldType));
        }

        _memoized[type] = answer;
    }

    return answer;
}
0 голосов
/ 29 декабря 2018

В соответствии с unmanaged документацией ограничения:

Тип unmanaged - это тип, который не является ссылочным типом и не содержит полей ссылочного типа ни при каких условиях.уровень вложенности.

Также в документации по проектированию языка C # упоминается ограничение неуправляемого типа :

Чтобы выполнить это ограничение, тип долженбыть структурой, и все поля типа должны относиться к одной из следующих категорий:

  • Иметь тип sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, IntPtr или UIntPtr.
  • быть любого типа enum.
  • быть типом указателя.
  • быть определенной пользователем структуройкоторая удовлетворяет ограничению unmanaged.

Соображения

Обычно вызов MakeGenericType является наиболее надежным решением для проверки универсального типаограничения, которые enfoпо CRL.Обычно пытаться выполнить проверку самостоятельно не очень хорошая идея, потому что может быть много правил, которые вы должны рассмотреть, и всегда есть шанс пропустить некоторые из них.Но имейте в виду, что, по крайней мере, во время написания этого ответа он не работает хорошо для ограничения unmanaged.

.NET Core имеет RuntimeHelpers.IsReferenceOrContainsReferences, но на момент написания этогоответ .NET Framework не имеет такой функции.Я должен отметить, что даже использование IsReferenceOrContainsReferences не совсем надежно для этой задачи.

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

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

Вариант 1 - ИспользованиеMakeGenericType

В качестве опции, чтобы проверить, может ли тип удовлетворять ограничению unmanaged, вы можете использовать следующий IsUnmanaged метод расширения '.

Примечание: Он должен быть более надежным, но я должен сказать, что это не так.Кажется, для ограничения unmanaged CLR не соблюдает ограничение и это просто функция компилятора C #.Поэтому, по крайней мере, на данный момент, я рекомендую использовать второй вариант.

using System;
using System.Reflection;
public static class UnmanagedTypeExtensions
{
    class U<T> where T : unmanaged { }
    public static bool IsUnManaged(this Type t)
    {
        try { typeof(U<>).MakeGenericType(t); return true; }
        catch (Exception){ return false; }
    }
}

Вариант 2. Написание собственного метода проверки документированных правил

В качестве другого варианта вы можете написать свой метод, проверяя документированные правила для ограничения unmanaged.Следующий код содержит больше правил, чем других ответов, чтобы иметь возможность обрабатывать такие случаи, как int? или (int,int):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class UnmanagedTypeExtensions
{
    private static Dictionary<Type, bool> cachedTypes =
    new Dictionary<Type, bool>();
    public static bool IsUnManaged(this Type t)
    {
        var result = false;
        if (cachedTypes.ContainsKey(t))
            return cachedTypes[t];
        else if (t.IsPrimitive || t.IsPointer || t.IsEnum)
            result = true;
        else if (t.IsGenericType || !t.IsValueType)
            result = false;
        else
            result = t.GetFields(BindingFlags.Public | 
               BindingFlags.NonPublic | BindingFlags.Instance)
                .All(x => x.FieldType.IsUnManaged());
        cachedTypes.Add(t, result);
        return result;
    }
}

Дополнительная информация

Вы можетеполезны следующие ссылки:

...