Есть ли способ сделать динамическое неявное приведение типов в С#?

Учитывая этот класс с неявным оператором приведения:

public class MyDateTime
{
    public static implicit operator MyDateTime(System.Int64 encoded)
    {
        return new MyDateTime(encoded);
    }

    public MyDateTime(System.Int64 encoded)
    {
        _encoded = encoded;
    }
    System.Int64 _encoded;
}

Теперь я могу сделать следующее:

long a = 5;
MyDateTime b = a;

Но НЕ следующее:

long f = 5;
object g = f;
MyDateTime h = g;

Это дает время компиляции:

Невозможно неявно преобразовать тип «объект» в «MyDateTime».

Имеет смысл для меня.

Теперь я модифицирую предыдущий пример следующим образом:

long f = 5;
object g = f;
MyDateTime h = (MyDateTime)g;

Это компилируется нормально. Теперь я получаю среду выполнения InvalidCastException:

Невозможно преобразовать объект типа «System.Int64» в тип MyDateTime.

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

Мои вопросы:

  1. Я прав?
  2. Есть ли другой способ сделать это?

Кстати, полное приложение заключается в том, что я использую Delegate.DynamicInvoke() для вызова функции, которая принимает параметр MyDateTime, а тип аргумента, который я передаю в DynamicInvoke, является длинным.


person Eric    schedule 18.01.2010    source источник


Ответы (3)


Я прав?

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

Есть ли другой способ сделать это?

да. В C# 4, если вы вводите свой «объект» как «динамический», мы снова запускаем компилятор во время выполнения и повторно выполняем весь анализ операндов, как будто их типы времени компиляции были текущими типами времени выполнения. Как вы можете себе представить, это недешево, хотя мы очень умно относимся к кэшированию и повторному использованию результатов, если вы делаете это в узком цикле.

person Eric Lippert    schedule 19.01.2010
comment
Просто из любопытства, по каким причинам операторы не ведут себя как методы во время выполнения? Учитывая ваш ответ, кажется, что они просто синтаксический сахар и не более того. На мой взгляд, это сделало бы C# еще более мощным, если бы такого рода операторы продвигались как первоклассные члены любого типа (что означает, что они стали бы частью всех других объектно-ориентированных достоинств, таких как наследование, переопределение и взаимодействие). Будет ли их использование во время выполнения по умолчанию означать серьезные изменения в дизайне и реализации языка? - person MarioDS; 19.02.2016
comment
@MDeSchaepmeester: Вы правы в том, что здесь есть некоторый разрыв. Например, сравните дизайн int с десятичным числом. Вы можете сказать Func<decimal, decimal> add = decimal.Add;, но нет способа сделать то же самое для int; вы должны сказать Func<int, int> add = (x, y) => x + y. Я думаю, было бы неплохо, если бы все встроенные типы были спроектированы как Decimal, а затем среда выполнения или компилятор могли бы понизить их до более фундаментальных операций из соображений производительности. Но такая последовательность действительно следует из функционального мышления. - person Eric Lippert; 19.02.2016
comment
@MDeSchaepmeester: я подозреваю, что первоначальные разработчики среды выполнения просто не думали о такой объединяющей функциональной абстракции при разработке системы типов и операций над встроенными типами. И, конечно же, странно, что в decimal есть как оператор +, так и метод Add. И даже не заставляйте меня рассказывать о дюжине способов представления операции равенства! Это немного беспорядок действительно. В следующий раз, когда вы будете разрабатывать фреймворк с нуля, сделайте все правильно заранее. - person Eric Lippert; 19.02.2016
comment
@MDeSchaepmeester: Кроме того, как вы заметили, ни один из этих материалов не работает с интерфейсами. Часто предлагается добавить статические интерфейсы, чтобы можно было сказать Matrix<T> where T : IAddable<T, T> и тому подобное. Это действительно потребовало бы довольно серьезной работы над языком и средой выполнения, поэтому это никогда не было очень высоким в списке приоритетов. - person Eric Lippert; 19.02.2016
comment
@EricLippert: MiscUtil Джона Скита предоставляет общие операторы (например, Operator.Add<T> и Operator.Add<TArg1,TArg2>) в относительно чистый способ. Это дает вам общие функции операторов, но не обеспечивает безопасность типов, что более важно. Однако вы можете получить аналогичную защиту с помощью контрактов кода. - person Brian; 19.02.2016
comment
Можно ли как-то использовать динамическое неявное преобразование в Expression Lambda? У меня есть что-то вроде Expression.Lambda(delegateType, Expression.Convert(getValExpr, resType), exprParameters), но результаты processCallExpr и object. Пример: тип из processCall — int, тип resType — double, и мне нужно какое-то выражение для его неявного преобразования вместо ошибки приведения. - person mvorisek; 24.03.2019

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

long f = 5;
object g = f;
MyDateTime h = g as MyDateTime;
person Roman Reiner    schedule 30.01.2013
comment
Он будет работать нормально в том смысле, что он не будет выдавать InvalidCastException, однако h будет null, что не то, чего хочет или ожидает OP. - person MarioDS; 19.02.2016

Добавление явного оператора должно работать: http://msdn.microsoft.com/en-us/library/85w54y0a(VS.80).aspx

person Pete    schedule 19.01.2010