Приведення типів і перетворення типів - це різні способи змінити вираз з одного типу даних на інший.
У документації C# перетворення типів класифікують як використання оператора as або оператора is. Приведення типів визначають як використання оператора приведення.
У C# дуже часто, поза межами числових значень і ієрархій класів, нам доведеться виконувати перетворення, викликаючи якийсь член цільового типу, наприклад Int32.Parse(), який перетворює рядок тексту (англ. string) на ціле число, або викликаючи член вихідного типу, наприклад object.ToString(). Про це варто памʼятати розробникам JavaScript та інших динамічних мов.
Зауважимо, що доступні оператори неявного та явного приведення operators (про них ідеться в operator-overloading), які дають змогу додати досить довільне приведення до власних типів.
Система типів C# дещо суворіша, ніж у C або JavaScript, і як наслідок операції приведення більш обмежені. Неявне приведення відбувається між двома числовими типами, якщо цільовий тип може зберегти діапазон і знак значення вихідного типу. Зауважимо в документації виняток для перетворення на дійсні числа, де точність може бути втрачена.
Неявне приведення не позначається якимось спеціальним синтаксисом. Наприклад:
int myInt = 1729;
long myLong = myInt;
Немає неявного перетворення числового виразу (або рядка тексту) на bool. Бібліотека базових класів (BCL) надає Convert.ToBoolean() для цієї мети.
Вираз типу char можна неявно привести до int. Приведення в протилежному напрямку має бути явним. Не всі значення int є припустимими символами UTF-16.
Якщо числові типи не можна неявно привести, зазвичай можна використати оператор явного приведення.
Якщо значення, яке приводиться, неможливо подати цільовим типом, бо він недостатньо широкий або виникає конфлікт знаків, тоді для цілих чисел може викидатися виняток переповнення, а для float і double змінна цільового типу може набути значення Infinity.
Вираз типу int можна явно привести до char. Це може призвести до недійсного char.
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
float largeFloat = float.MaxValue;
float smallFloat = 17.29f;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
int from_char = 'a'; // 96
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
int from_smallFloat = (int) smallFloat; // 17
int from_largeFloat = (int) largeFloat; // -2147483648 or OverflowException is thrown (if checked)
char from_intc = (char) 32; // ' '
char from_invalid_int = (char) 0xdcbf; // invalid char - no exception thrown
// no cast available
int fromString = Int32.Parse("42"); // 42
string toString = largeInt.ToString(); // "2147483647"
int fromString_bad = Int32.Parse("forty two"); // FormatException is thrown
Додатково про ключове слово checked можна прочитати в цій статті.
Будь-який тип можна неявно перетворити на його базовий клас або інтерфейс.
IList<int> ll = new List<int>();
Оператор приведення можна використати, щоб перетворити базовий клас на похідний.
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
Якщо приведення не вдається, бо тип виразу, який приводиться, не повʼязаний з типом, до якого його приводять, викидається виняток InvalidCastException.
Звичніший підхід до таких перетворень типів - використати ключове слово is:
interface IFoo { int Bar(); }
class Foo : IFoo { public int Bar() { return 42; }}
Object r = new Random();
IFoo ifoo = new Foo();
Object o = new Foo();
int result = 1729;
if (ifoo is Foo foo)
{
result = foo.Bar();
}
// result == 42
result = 1729;
if (o is Foo foo2)
{
result = foo2.Bar();
}
// result == 42
result = 1729;
if (r is Foo foo3)
{
result = foo3.Bar();
}
// result == 1729
Ключове слово as виконує подібну функцію до is, наприклад: var foo = ifoo as Foo;. У цьому прикладі foo буде null, якщо ifoo не має типу Foo; інакше ifoo буде присвоєно йому.
Типи можуть визначати власні явні та неявні оператори приведення.
Приклади явних і неявних приведень у BCL - це перетворення зі структури BigInteger на інші числові типи та навпаки.
typeof
Якщо потрібно визначити точний тип обʼєкта, то is може бути трохи занадто поблажливим, адже він перетворює обʼєкт на клас або будь-який з його базових класів. У цьому випадку допоможуть typeof і Object.GetType().
object o = new List<int>();
o is ICollection<int> // true
o.GetType() == typeof(ICollection<int>) // false
o is List<int> // true
o.GetType() == typeof(List<int>) // true