C # 4.0 представив новий тип під назвою "динамічний". Все це звучить добре, але для чого програміст це використає?
Чи є ситуація, коли це може врятувати день?
C # 4.0 представив новий тип під назвою "динамічний". Все це звучить добре, але для чого програміст це використає?
Чи є ситуація, коли це може врятувати день?
Відповіді:
Динамічне ключове слово є новим для C # 4.0 і використовується для того, щоб сказати компілятору, що тип змінної може змінюватися або що вона не відома до часу виконання. Подумайте про це як про можливість взаємодіяти з Об'єктом без необхідності його кидати.
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
Зверніть увагу, що нам не потрібно було видавати і не заявляти про службу як клієнта типу. Оскільки ми оголосили це динамічним, час виконання бере на себе, а потім шукає та встановлює для нас властивість FirstName. Тепер, звичайно, коли ви використовуєте динамічну змінну, ви відмовляєтесь від перевірки типу компілятора. Це означає, що виклик cust.MissingMethod () буде компілюватися та не виходити з ладу до часу виконання. Результатом цієї операції є RuntimeBinderException, оскільки MissingMethod не визначений у класі клієнта.
Наведений вище приклад показує, як працює динаміка під час виклику методів та властивостей. Ще одна потужна (і потенційно небезпечна) функція - це можливість повторного використання змінних для різних типів даних. Я впевнений, що програмісти Python, Ruby та Perl там можуть придумати мільйон способів скористатися цим, але я використовую C # так довго, що він просто відчуває себе "неправильно".
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 набирається як подвійний, а рядок 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
dynamic
c в для вирішення завдань, які можна вирішити (можливо, навіть краще) за допомогою стандартних функцій c # та статичного введення тексту, або, принаймні, з висновком типу ( var
). dynamic
слід використовувати лише тоді, коли мова йде про проблеми взаємодії з DLR. Якщо ви пишете код на мові статичного типу, як, наприклад, c # є, зробіть це, і не імітуйте динамічну мову. Це просто потворно.
dynamic
широко використовуєте змінні у своєму коді там, де вони вам не потрібні (як у вашому прикладі з квадратною коренею), ви відмовитесь від чистої перевірки помилок часу компіляції; замість цього ви отримуєте можливі помилки під час виконання.
dynamic
Ключове слово додано, разом з багатьма іншими новими функціями C # 4.0, щоб зробити його простіше говорити з кодом , який живе в або приходить від інших середовищ виконання, який має різні інтерфейси.
Візьмемо приклад.
Якщо у вас є об'єкт 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);
Зверніть увагу на всі ці аргументи? Їх потрібно передавати, оскільки C # до версії 4.0 не мала поняття необов'язкових аргументів. У C # 4.0, COM API було спрощено працювати, ввівши:
ref
додатковим для API 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
Магія полягає в тому, що компілятор C # тепер буде вводити необхідний код і працювати з новими класами під час виконання, щоб зробити майже те саме, що ви робили раніше, але синтаксис був прихований від вас, тепер ви можете зосередитись на що , і не стільки на те, як . Андерс Хейльсберг любить говорити, що ви повинні викликати різні "заклинання", що є своєрідним каламбуром над магією всієї справи, де вам зазвичай потрібно махати рукою (ами) і вимовляти якісь магічні слова в потрібному порядку щоб отримати певний тип заклинання. Старий спосіб спілкування API з об’єктами COM був дуже багато, вам потрібно було перестрибувати безліч обручів, щоб перемогти компілятор, щоб скласти код для вас.
У C # перед версією 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 мав необов'язкові аргументи та підтримку більшості цього поза межами вікна, тому цей C # код:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
це, по суті, лише C # наздоганяє VB з точки зору виразності, але робить це правильним шляхом, роблячи його розширюваним, а не лише для COM. Звичайно, це також доступно для VB.NET або будь-якої іншої мови, побудованої на версії .NET виконання.
Ви можете знайти більше інформації про IDispatch
інтерфейс у Вікіпедії: IDispatch, якщо хочете прочитати більше про нього. Це справді горішні речі.
Однак що робити, якщо ви хочете поговорити з об’єктом Python? Для цього існує інший API, ніж той, що використовується для COM-об'єктів, і оскільки об’єкти Python також є динамічними за своєю природою, вам потрібно вдатися до магії відображення, щоб знайти правильні методи виклику, їх параметри тощо, але не .NET рефлексія, щось написане для Python, дуже схоже на код IDispatch вище, просто зовсім інший.
А для Рубі? Інший API досі.
JavaScript? Та ж угода, різні API і для цього.
Динамічне ключове слово складається з двох речей:
dynamic
dynamic
ключового слова, і відображає виклики до правильного способу виконання дій. API навіть задокументований, тому якщо у вас об’єкти, що надходять із часу виконання, не охоплені, ви можете додати його.Однак, dynamic
ключове слово не означає замінити будь-який існуючий код .NET. Звичайно, ви можете це зробити, але це не було додано з цієї причини, і автори мови програмування C # з Андерсом Хейлсбергом на передньому плані були найбільш прихильними, що вони все ще вважають C # сильно набраною мовою, і не принесуть шкоди. цей принцип.
Це означає, що хоча ви можете написати такий код:
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
і якщо вона компілюється, це не малося на увазі як якийсь тип магії-дозволяє-зрозуміти-що-що ти мав на увазі під час виконання системи.
Вся мета полягала в тому, щоб полегшити розмову з іншими типами предметів.
В Інтернеті є багато матеріалів про ключові слова, прихильників, опонентів, дискусії, мітинги, похвали тощо.
Я пропоную вам почати з наступних посилань, а потім google для отримання додаткової інформації:
dynamic
було додано, щоб підтримати інші екосистеми щодо того, як можна зробити виклик методу, що викликає роздуми, а також забезпечити своєрідний підхід до структури даних із чорним ящиком із задокументованим способом досягнення цього.
Я здивований, що ніхто не згадував багаторазову відправлення . Звичайний спосіб обійти це за допомогою шаблону відвідувачів, і це не завжди можливо, тому ви закінчите складеними 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
об’єкти марними атрибутами серіалізації.
is
складанням один на інший.
magic
; не існує такого поняття, як магія.
Це полегшує взаємодію статичних мов (CLR) з динамічними (python, ruby ...), що працюють на DLR (динамічний час виконання мови), див. MSDN :
Наприклад, ви можете використовувати наступний код для збільшення лічильника XML у C #.
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
Використовуючи DLR, ви можете використовувати наступний код замість тієї ж операції.
scriptobj.Count += 1;
MSDN перераховує ці переваги:
- Спрощує перенесення динамічних мов до .NET Framework
- Вмикає динамічні функції в статично набраних мовах
- Забезпечує майбутні переваги DLR та .NET Framework
- Вмикає обмін бібліотеками та об’єктами
- Забезпечує швидку динамічну відправлення та виклик
Докладнішу інформацію див. У MSDN .
Приклад використання:
Ви споживаєте багато класів, які мають властивість комунікації '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; }
}
Якщо ви пишете метод комунікації, який отримує значення властивості 'CreationDate', вам доведеться використовувати відображення:
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
З концепцією "динамічного" ваш код набагато елегантніший:
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
Він здебільшого буде використовуватися жертвами RAD та Python для знищення якості коду, IntelliSense та компіляції виявлення помилок у часі.
Він оцінюється під час виконання, тому ви можете переключити тип JavaScript, як ви можете в JavaScript, на все, що завгодно. Це законно:
dynamic i = 12;
i = "text";
І так ви можете змінити тип, як вам потрібно. Використовуйте його в крайньому випадку; це вигідно, але я чув, що багато чого відбувається на сцені з точки зору генерованого ІЛ, і це може прийти до продуктивних цін.
Найкращим варіантом використання змінних типу "динамічний" для мене був те, коли нещодавно я писав рівень доступу до даних в ADO.NET ( використовуючи SQLDataReader ), і код викликав уже написані застарілі збережені процедури. Є сотні тих застарілих зберігаються процедур, що містять основну частину ділової логіки. Моєму шару доступу до даних потрібно було повернути якісь структуровані дані на рівень бізнес-логіки, заснований на C #, щоб зробити деякі маніпуляції ( хоча їх майже немає ). Кожна збережена процедура повертає різний набір даних ( стовпці таблиці ). Отже, замість створення десятків класів або структур для зберігання повернутих даних і передачі їх до BLL, я написав код нижче, який виглядає досить елегантно і акуратно.
public static dynamic GetSomeData(ParameterDTO dto)
{
dynamic result = null;
string SPName = "a_legacy_stored_procedure";
using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
{
SqlCommand command = new SqlCommand(SPName, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
dynamic row = new ExpandoObject();
row.EmpName = reader["EmpFullName"].ToString();
row.DeptName = reader["DeptName"].ToString();
row.AnotherColumn = reader["AnotherColumn"].ToString();
result = row;
}
}
}
return result;
}
dynamic np = Py.Import("numpy")
dynamic
при застосуванні до них числових операторів. Це забезпечує безпеку типу та дозволяє уникнути обмеження дженериків. Це по суті * качка типу:T y = x * (dynamic)x
, де typeof(x) is T
Іншим випадком використання для dynamic
введення тексту є віртуальні методи, у яких виникають проблеми з коваріацією чи противаріантністю. Одним із таких прикладів є сумнозвісний Clone
метод, який повертає об'єкт того ж типу, що і об'єкт, на який він викликається. Ця проблема не вирішується повністю з динамічним поверненням, оскільки вона минає перевірку статичного типу, але принаймні вам не потрібно весь час використовувати некрасиві касти при використанні звичайного object
. Інакше сказати, касти стають неявними.
public class A
{
// attributes and constructor here
public virtual dynamic Clone()
{
var clone = new A();
// Do more cloning stuff here
return clone;
}
}
public class B : A
{
// more attributes and constructor here
public override dynamic Clone()
{
var clone = new B();
// Do more cloning stuff here
return clone;
}
}
public class Program
{
public static void Main()
{
A a = new A().Clone(); // No cast needed here
B b = new B().Clone(); // and here
// do more stuff with a and b
}
}