Что такое "динамический" тип в С# 4.0?

В С# 4.0 введен новый тип, называемый динамическим. Все это звучит неплохо, но для чего его программист использовал?

Есть ли ситуация, когда он может сохранить день?

Ответ 1

Динамическое ключевое слово является новым для С# 4.0 и используется, чтобы сообщить компилятору, что тип переменной может измениться или что он неизвестен до выполнения. Подумайте об этом как о возможности взаимодействия с объектом без необходимости его бросить.

dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!

Обратите внимание, что нам не нужно было указывать или объявлять cust как тип Customer. Поскольку мы объявили его динамическим, среда выполнения берет на себя, а затем выполняет поиск и устанавливает для нас свойство FirstName. Теперь, конечно, когда вы используете динамическую переменную, вы отказываетесь от проверки типа компилятора. Это означает, что вызов cust.MissingMethod() будет компилироваться и не прерываться до времени выполнения. Результатом этой операции является исключение RuntimeBinderException, поскольку MissingMethod не определено в классе Customer.

В приведенном выше примере показано, как динамически работает при вызове методов и свойств. Другая мощная (и потенциально опасная) функция позволяет повторно использовать переменные для разных типов данных. Я уверен, что программисты Python, Ruby и Perl могут подумать о миллионах способов воспользоваться этим, но я использую С# так долго, что он просто чувствует себя "неправильно" для меня.

dynamic foo = 123;
foo = "bar";

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

decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");

Вторая строка не компилируется, потому что 2.5 набирается как double, а строка 3 не компилируется, потому что Math.Sqrt ожидает двойной. Очевидно, все, что вам нужно сделать, это бросить и/или изменить тип переменной, но могут быть ситуации, в которых динамика имеет смысл использовать.

dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");

Подробнее функция: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

Ответ 2

Было добавлено ключевое слово dynamic, а также множество других новых функций С# 4.0, чтобы упростить разговор с кодом, который живет или используется в других средах, имеющих разные API.

Возьмем пример.

Если у вас есть COM-объект, например объект Word.Application, и вы хотите открыть документ, способ для этого должен иметь не менее 15 параметров, большинство из которых являются необязательными.

Чтобы вызвать этот метод, вам понадобится что-то вроде этого (я упрощаю, это не настоящий код):

object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing);

Обратите внимание на все эти аргументы? Вам нужно передать их с С#, прежде чем версия 4.0 не имеет понятия необязательных аргументов. В С# 4.0 с API-интерфейсом COM стало легче работать, введя:

  • Дополнительные аргументы
  • Создание ref опционально для COM-API
  • Именованные аргументы

Новый синтаксис для вышеуказанного вызова:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

Посмотрите, насколько проще это выглядит, насколько более читаемым оно становится?

Отбросьте это отдельно:

                                    named argument, can skip the rest
                                                   |
                                                   v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
                                 ^                         ^
                                 |                         |
                               notice no ref keyword, can pass
                               actual parameter values instead

Магия в том, что компилятор С# теперь будет вводить необходимый код и работать с новыми классами во время выполнения, чтобы выполнить почти ту же самую вещь, что и раньше, но синтаксис был скрыт от вас, теперь вы можете сосредоточьтесь на том, что, а не столько на том, как. Андерс Хейлсберг любит говорить, что вам приходится ссылаться на разные "заклинания", которые являются своего рода каламбуром магии всего этого, где вам обычно приходится махать рукой (руками) и произносить волшебные слова в правильном порядке чтобы получить определенный тип заклинания. Старый API-интерфейс для общения с объектами COM был таким, что вам нужно было перепрыгнуть через множество обручей, чтобы коаксировать компилятор для компиляции кода для вас.

В С# до версии 4.0 все больше и больше, если вы пытаетесь поговорить с COM-объектом, для которого у вас нет интерфейса или класса, все, что у вас есть, - это ссылка IDispatch.

Если вы не знаете, что это такое, IDispatch является в основном отражением для COM-объектов. С интерфейсом IDispatch вы можете задать объект "номер идентификатора для метода, известный как" Сохранить ", и создать массивы определенного типа, содержащие значения аргументов, и, наконец, вызвать метод Invoke на IDispatch, чтобы вызвать метод, передав всю информацию, которую вам удалось собрать.

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

string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);

Все это просто для открытия документа.

У VB были необязательные аргументы и поддержка большей части этого из коробки давным-давно, поэтому этот код С#:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

- это в основном просто С#, догоняющий VB с точки зрения выразительности, но делая это правильно, делая его расширяемым, а не только для COM. Конечно, это также доступно для VB.NET или любого другого языка, созданного поверх среды выполнения .NET.

Дополнительную информацию о интерфейсе IDispatch можно найти в Wikipedia: IDispatch, если вы хотите больше узнать об этом. Это действительно здорово.

Однако, что, если вы хотите поговорить с объектом Python? Для этого существует другой API, чем тот, который используется для COM-объектов, и поскольку объекты Python также динамичны по своей природе, вам нужно прибегнуть к магии отражения, чтобы найти правильные методы для вызова, их параметры и т.д., Но не .NET. отражение, написанное для Python, в значительной степени похожее на код IDispatch выше, просто совсем другой.

И для Руби? Другой API все еще.

JavaScript? Та же сделка, другой API для этого.

Динамическое ключевое слово состоит из двух элементов:

  • Новое ключевое слово в С#, dynamic
  • Набор классов времени выполнения, который знает, как обращаться с различными типами объектов, которые реализуют определенный API, для которого требуется ключевое слово dynamic, и сопоставляет вызовы с правильным способом выполнения действий. API даже документирован, поэтому, если у вас есть объекты, которые происходят из среды выполнения, которые не были включены, вы можете добавить его.

Ключевое слово dynamic, однако, не предназначено для замены любого существующего .NET-кода. Конечно, вы можете это сделать, но по этой причине он не был добавлен, и авторы языка программирования С# с Андерсом Хейлсбергом в передней части были самыми непреклоненными, что они по-прежнему рассматривают С# как строго типизированный язык и не будут жертвовать этот принцип.

Это означает, что хотя вы можете написать такой код:

dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;

и скомпилировать его, это не предназначалось как своего рода тип magic-lets-figure-out-what-you-mean-at-runtime.

Цель заключалась в том, чтобы облегчить общение с другими типами объектов.

В Интернете много материалов о ключевом слове, сторонниках, противниках, дискуссиях, тиражах, похвалах и т.д.

Я предлагаю вам начать со следующих ссылок, а затем google для более:

Ответ 3

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

Итак, вот пример реальной жизни приложения. Вместо этого:

public static MapDtoBase CreateDto(ChartItem item)
{
    if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
    if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
    if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
    //other subtypes follow
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

Вы делаете:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

private static MapDtoBase CreateDtoImpl(ChartItem item)
{
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

private static MapDtoBase CreateDtoImpl(MapPoint item)
{
    return new MapPointDto(item);
}

private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
    return new ElevationDto(item);
}

Обратите внимание, что в первом случае ElevationPoint является подклассом MapPoint, и если он не был помещен до MapPoint, он никогда не будет достигнут. Это не относится к динамике, так как будет вызываться метод ближайшего совпадения.

Как вы могли догадаться из кода, эта функция пригодилась, когда я выполнял перевод с объектов ChartItem в их сериализуемые версии. Я не хотел загрязнять свой код посетителями, и я также не хотел загрязнять объекты ChartItem бесполезными атрибутами сериализации.

Ответ 4

Это упрощает для статических типизированных языков (CLR) взаимодействие с динамическими (python, ruby ​​...), запущенными на DLR (динамическое время исполнения), см. MSDN.

Ответ 5

COM-взаимодействие. Особенно IUnknown. Он был специально разработан для него.

Ответ 6

В основном это будет использоваться жертвами RAD и Python для разрушения качества кода, IntelliSense и обнаружения ошибки времени компиляции.

Ответ 7

Пример использования:

Вы потребляете много классов, у которых есть свойство commation 'CreationDate':

public class Contact
{
    // some properties

    public DateTime CreationDate { get; set; }        
}

public class Company
{
    // some properties

    public DateTime CreationDate { get; set; }

}

public class Opportunity
{
    // some properties

    public DateTime CreationDate { get; set; }

}

Если вы напишете метод commun, который извлекает значение свойства CreationDate, вам нужно будет использовать отражение:

    static DateTime RetrieveValueOfCreationDate(Object item)
    {
        return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
    }

С концепцией "dynamic" ваш код намного элегантнее:

    static DateTime RetrieveValueOfCreationDate(dynamic item)
    {
        return item.CreationDate;
    }

Ответ 8

Он оценивается во время выполнения, поэтому вы можете переключать тип, как на JavaScript, на все, что захотите. Это законно:

dynamic i = 12;
i = "text";

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

Ответ 9

  • Вы можете вызывать динамические языки, такие как CPython, используя pythonnet:

dynamic np = Py.Import("numpy")

  1. При применении на них числовых операторов вы можете использовать generics для dynamic. Это обеспечивает безопасность типов и позволяет избежать ограничений генериков. Это, по сути, * утиная печать:

T y = x * (dynamic)x, где typeof(x) is T