как проверить присваиваемость типов во время выполнения в C #?

Класс Type имеет метод IsAssignableFrom (), который почти работает. К сожалению, он возвращает true, только если два типа совпадают или первый находится в иерархии второго. В нем говорится, что десятичное число нельзя назначить из int, но я бы хотел метод, который бы указывал, что десятичные числа можно назначать из целых, но не всегда можно назначить целые числа из десятичных. Компилятор знает это, но мне нужно выяснить это во время выполнения.

Вот тест для метода расширения.

    [Test]
    public void DecimalsShouldReallyBeAssignableFromInts()
    {
        Assert.IsTrue(typeof(decimal).IsReallyAssignableFrom(typeof(int)));
        Assert.IsFalse(typeof(int).IsReallyAssignableFrom(typeof(decimal)));
    }

Есть ли способ реализовать IsReallyAssignableFrom (), который бы работал как IsAssignableFrom (), но также проходил тестовый пример выше? Этот пример не компилируется для меня, поэтому мне пришлось установить Number на 0 (вместо 0.0M).

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
    public class MyAttribute : Attribute
    {
        public object Default { get; set; }
    }

    public class MyClass
    {
        public MyClass([MyAttribute(Default= 0.0M)] decimal number)
        {
            Console.WriteLine(number);
        }
    }

Я получаю эту ошибку: Ошибка 4 Аргументом атрибута должно быть константное выражение, Выражение typeof или выражение создания массива типа параметра атрибута

10
задан epicsmile 2 September 2010 в 14:40
поделиться

3 ответа

Этот почти работает... он использует выражения Linq:

public static bool IsReallyAssignableFrom(this Type type, Type otherType)
{
    if (type.IsAssignableFrom(otherType))
        return true;

    try
    {
        var v = Expression.Variable(otherType);
        var expr = Expression.Convert(v, type);
        return expr.Method == null || expr.Method.Name == "op_Implicit";
    }
    catch(InvalidOperationException ex)
    {
        return false;
    }
}

Единственный случай, который не работает, это встроенные преобразования для примитивных типов: он неправильно возвращает true для преобразований, которые должны быть явными (например, int в short). Я думаю, вы могли бы обрабатывать эти случаи вручную, так как их конечное (и довольно небольшое) количество.

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

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

На самом деле бывает так, что тип decimal не "присваивается" типу int, и наоборот. Проблемы возникают, когда задействована упаковка/распаковка.

Возьмем пример ниже:

int p = 0;
decimal d = 0m;
object o = d;
object x = p;

// ok
int a = (int)d;

// invalid cast exception
int i = (int)o;

// invalid cast exception
decimal y = (decimal)p;

// compile error
int j = d;

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

Причина, по которой присваивание a работает, заключается в том, что класс decimal имеет явное переопределение оператора приведения типа к int. Не существует оператора неявного приведения типа от decimal до int.

Редактировать: Не существует даже обратного неявного оператора. Int32 реализует IConvertible, и именно так он преобразуется в десятичное число. Конец редактирования

Другими словами, типы не назначаемые, а преобразуемые.

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

Удачи!

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

Ответ Тимви действительно полный, но я чувствую, что есть даже более простой способ, который даст вам та же семантика (проверьте «реальную» присваиваемость), фактически не определяя себе, что это является.

Вы можете просто попробовать задание и найти InvalidCastException (я знаю, что это очевидно). Таким образом, вы избегаете хлопот проверки трех возможных значений присваиваемости, как упоминал Тимви. Вот пример использования xUnit:

[Fact]
public void DecimalsShouldReallyBeAssignableFromInts()
{
    var d = default(decimal);
    var i = default(i);

    Assert.Throws<InvalidCastException)( () => (int)d);
    Assert.DoesNotThrow( () => (decimal)i);
}
0
ответ дан 3 December 2019 в 22:34
поделиться
Другие вопросы по тегам:

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