Навіть якщо ви можете бачити їх якось рівнозначними, вони абсолютно різні за призначенням. Спершу спробуємо визначити, що таке акторський склад:
Кастинг - це дія зміни об’єкта одного типу даних на інший.
Це трохи загально, і це якимось чином еквівалентно перетворенню, тому що привід часто має однаковий синтаксис перетворення, тому питання має полягати в тому, коли закидання (неявне чи явне) дозволено мовою, і коли потрібно використовувати ( більше) явне перетворення?
Дозвольте мені спочатку провести просту межу між ними. Формально (навіть якщо це еквівалентно для синтаксису мови) привід змінить тип, тоді як перетворення змінить / може змінити значення (з часом разом із типом). Також акторський склад є оборотним, тоді як конвертація може не бути.
Ця тема досить велика, тому спробуємо трохи звузити її, виключивши спеціальні оператори акторського складу з гри.
Неявний кидок
У C # привід є неявним, коли ви не втратите жодної інформації (зверніть увагу, що ця перевірка виконується з типами, а не з їх фактичними значеннями ).
Первісні типи
Наприклад:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
Ці перекиди неявні, оскільки під час перетворення ви не втратите жодної інформації (ви просто робите тип ширшим). Навпаки, неявний прийом не дозволяється, оскільки, незалежно від їх фактичних значень (оскільки їх можна перевірити лише під час виконання), під час перетворення ви можете втратити деяку інформацію. Наприклад, цей код не компілюється, оскільки a doubleможе містити (і насправді це робить) значення, яке не можна представити за допомогою float:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Об'єкти
У випадку об'єкта (вказівник на) приведення завжди неявне, коли компілятор може бути впевнений, що тип джерела є похідним класом (або він реалізує) типом цільового класу, наприклад:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
У цьому випадку компілятор знає, що stringреалізує, IFormattableа NotSupportedExceptionце (походить від), Exceptionтому приведення є неявним. Інформація не втрачається, оскільки об'єкти не змінюють своїх типів (це відрізняється від structs і примітивних типів, оскільки за допомогою прив'язки ви створюєте новий об'єкт іншого типу ), що змінює ваш погляд на них.
Відвертий привід
Привід явний, коли перетворювач не виконується неявно компілятором, і тоді ви повинні використовувати оператор приведення. Зазвичай це означає, що:
- Ви можете втратити інформацію або дані, тому вам доведеться про це знати.
- Перетворення може бути невдалим (оскільки ви не можете перетворити один тип на інший), тому знову ж таки, ви повинні бути в курсі того, що робите.
Первісні типи
Явний привід потрібен для примітивних типів, коли під час перетворення ви можете втратити деякі дані, наприклад:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;
float epsilon = (float)Double.Epsilon;
В обох прикладах, навіть якщо значення потрапляють у floatдіапазон, ви втратите інформацію (в даному випадку точність), тому перетворення має бути явним. Тепер спробуйте це:
float max = (float)Double.MaxValue;
Це перетворення не вдасться, тому, знову ж таки, воно повинно бути явним, тому ви знаєте про це і можете зробити перевірку (у прикладі значення є постійним, але воно може надходити з деяких обчислень часу виконання або вводу-виводу). Повернімось до вашого прикладу:
string text = "123";
double value = (double)text;
Це не компілюється, оскільки компілятор не може перетворити текст у числа. Текст може містити будь-які символи, не лише цифри, і це занадто багато, в C #, навіть для явного приведення (але це може бути дозволено іншою мовою).
Об'єкти
Перетворення з покажчиків (на об'єкти) можуть зазнати невдачі, якщо типи не пов'язані між собою, наприклад, цей код не компілюється (оскільки компілятор знає, що неможливе перетворення):
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Цей код буде скомпільовано, але він може не вдатися під час виконання (це залежить від ефективного типу залитих об'єктів) із InvalidCastException:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Перетворення
Отже, нарешті, якщо касти - це перетворення, то навіщо нам потрібні такі класи Convert? Ігноруючи тонкі відмінності, що виникають від Convertреалізації та IConvertibleреалізацій, насправді тому, що в C # із складанням ви говорите компілятору:
повірте, цей тип - це той тип, навіть якщо ви зараз цього не можете знати, дозвольте мені це зробити, і ви побачите.
-або-
не хвилюйся, мені байдуже, що щось загубиться в цьому перетворенні.
Для всього іншого потрібна більш чітка операція (подумайте про наслідки легких приводів , тому C ++ ввів для них довгий, багатослівний та явний синтаксис). Це може включати складну операцію (для string-> doubleперетворення знадобиться синтаксичний розбір). Наприклад, перетворення на string, наприклад, завжди можливо (за допомогою ToString()методу), але це може означати щось інше, ніж те, що ви очікуєте, тому воно має бути більш явним, ніж акторський склад ( більше ви пишете, більше думаєте про те, що робите ).
Це перетворення можна виконати всередині об'єкта (використовуючи для цього відомі інструкції IL), використовуючи власні оператори перетворення (визначені в класі для приведення) або більш складні механізми (наприклад, TypeConverters або методи класу). Ви не знаєте, що станеться з цим, але ви знаєте, що це може провалитися (тому IMO, коли можливе більш контрольоване перетворення, ви повинні використовувати його). У вашому випадку перетворення просто проаналізує, stringщоб отримати double:
double value = Double.Parse(aStringVariable);
Звичайно, це може не вдатися, тому якщо ви це зробите, ви завжди повинні ловити виняток, який він може викинути ( FormatException). Тут це поза темою, але коли a TryParseє, тоді вам слід його використовувати (оскільки семантично ви говорите, що це не число, і це навіть швидше ... не вдається).
Перетворення в .NET можуть надходити з багатьох місць, TypeConverterнеявні / явні приведення з визначеними користувачем операторами перетворення, реалізація IConvertibleта методи синтаксичного аналізу (я щось забув?). Подивіться на MSDN, щоб дізнатися більше про них.
Щоб закінчити цю довгу відповідь, лише кілька слів про визначені користувачем оператори перетворення. Це просто цукор - дозволити програмісту використовувати гіпс для перетворення одного типу на інший. Це метод всередині класу (той, який буде відлитий), який говорить "привіт, якщо він / вона хоче перетворити цей тип на цей тип, я можу це зробити". Наприклад:
float? maybe = 10;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
У цьому випадку це явно, оскільки воно може не вдатися, але це дозволено реалізації (навіть якщо про це є вказівки). Уявіть, що ви пишете власний клас рядків таким чином:
EasyString text = "123";
double value = (string)text;
У своїй реалізації ви можете вирішити "полегшити життя програміста" і виставити цю конверсію за допомогою складання (пам'ятайте, це просто ярлик, щоб писати менше). Деякі мови можуть навіть дозволити таке:
double value = "123";
Дозволяє неявне перетворення в будь-який тип (перевірка буде виконуватися під час виконання). За допомогою відповідних параметрів це можна зробити, наприклад, у VB.NET. Це просто інша філософія.
Що я можу з ними робити?
Отже, останнє питання - коли слід користуватися тим чи іншим. Давайте подивимося, коли ви можете використовувати явний привід:
- Перетворення між базовими типами.
- Перетворення з
objectна будь-який інший тип (сюди може входити і розпакування).
- Перетворення з похідного класу в базовий клас (або в реалізований інтерфейс).
- Перетворення з одного типу на інший за допомогою спеціальних операторів перетворення.
Тільки перша конверсія може бути виконана, Convertтому для інших у вас немає вибору, і вам потрібно використовувати явний привід.
Давайте подивимось зараз, коли ви можете використовувати Convert:
- Перетворення з будь-якого базового типу на інший базовий тип (з деякими обмеженнями, див. MSDN ).
- Перетворення з будь-якого типу, який реалізується,
IConvertibleна будь-який інший (підтримуваний) тип.
- Перетворення з / у
byteмасив у / із рядка.
Висновки
ІМО Convertслід використовувати кожного разу, коли ви знаєте, що перетворення може бути невдалим (через формат, через діапазон або через те, що воно не підтримується), навіть якщо те саме перетворення можна виконати за допомогою приведення (якщо не доступне щось інше). Зрозуміло, хто буде читати ваш код, що ви маєте намір і що він може не вдатися (спрощення налагодження).
Для всього іншого вам потрібно використовувати гіпс, немає вибору, але якщо доступний інший кращий метод, то я пропоную вам використовувати його. У вашому прикладі перетворення з stringу double- це те, що (особливо якщо текст надходить від користувача) дуже часто не вдається, тому ви повинні зробити це якомога явнішим (крім того, ви отримуєте більше контролю над ним), наприклад, використовуючи TryParseметод.
Редагувати: яка різниця між ними?
Згідно з оновленим запитанням та збереженням того, що я писав раніше (про те, коли можна використовувати привід порівняно з тим, коли можна / доведеться використовувати Convert), останній пункт, який слід уточнити, полягає в тому, чи є між ними різниця (крім того, Convertвикористання IConvertibleта IFormattableінтерфейси, щоб він міг виконувати операції не дозволяється з акторським складом).
Коротка відповідь - так, вони поводяться по-різному . Я бачу Convertклас як клас допоміжних методів, тому часто він дає певні переваги або дещо іншу поведінку. Наприклад:
double real = 1.6;
int castedInteger = (int)real;
int convertedInteger = Convert.ToInt32(real);
Зовсім інакше, так? Актори зрізаються (це те, чого ми всі очікуємо), але Convertвиконує округлення до найближчого цілого числа (і цього може не сподіватися, якщо ви про це не знаєте). Кожен метод перетворення вводить відмінності, тому загальне правило не може бути застосовано, і їх слід розглядати в кожному конкретному випадку ... 19 базових типів для перетворення в будь-який інший тип ... список може бути досить довгим, набагато краще проконсультуватися у випадку MSDN за справа!