Нижче розміщена публікація з наступної статті :
Різницею між примусом і кастингом часто нехтують. Я розумію, чому; багато мов мають однаковий (або подібний) синтаксис та термінологію для обох операцій. Деякі мови можуть навіть позначати будь-яке перетворення як "кастинг", але наступне пояснення стосується понять в CTS.
Якщо ви намагаєтесь присвоїти значення певного типу розташуванню іншого типу, ви можете сформувати значення нового типу, яке має подібне значення до оригіналу. Це примус. Примус дозволяє використовувати новий тип, створюючи нове значення, яке певним чином нагадує оригінал. Деякі примуси можуть відкидати дані (наприклад, перетворюючи int 0x12345678 на короткий 0x5678), а інші - ні (наприклад, перетворюючи int 0x00000008 на короткий 0x0008, або довгий 0x0000000000000008).
Нагадаємо, що значення можуть мати кілька типів. Якщо ваша ситуація дещо інша, і ви хочете вибрати лише один із типів значення, кастинг - це інструмент для роботи. Кастинг просто вказує, що ви хочете оперувати певним типом, який включає значення.
Різниця на рівні коду варіюється від C # до IL. У C # і кастинг, і примус виглядають досить схожими:
static void ChangeTypes(int number, System.IO.Stream stream)
{
long longNumber = number;
short shortNumber = (short)number;
IDisposable disposableStream = stream;
System.IO.FileStream fileStream = (System.IO.FileStream)stream;
}
На рівні ІЛ вони досить різні:
ldarg.0
conv.i8
stloc.0
ldarg.0
conv.i2
stloc.1
ldarg.1
stloc.2
ldarg.1
castclass [mscorlib]System.IO.FileStream
stloc.3
Що стосується логічного рівня, тут є кілька важливих відмінностей. Найголовніше пам’ятати, що примус створює нову цінність, а кастинг - ні. Ідентичність вихідного значення та значення після відливання однакові, тоді як ідентифікація примусового значення відрізняється від вихідного; примус створює новий, чіткий екземпляр, тоді як кастинг - ні. Висновок полягає в тому, що результат кастингу та оригінал завжди будуть еквівалентні (як за ідентичністю, так і за рівністю), але примусове значення може бути рівним оригіналу, а може і не бути, і ніколи не поділяє оригінальну ідентичність.
На прикладі вище легко побачити наслідки примусу, оскільки числові типи завжди копіюються за значенням. Справи стають дещо складнішими, коли ви працюєте з еталонними типами.
class Name : Tuple<string, string>
{
public Name(string first, string last)
: base(first, last)
{
}
public static implicit operator string[](Name name)
{
return new string[] { name.Item1, name.Item2 };
}
}
У наведеному нижче прикладі одне перетворення - це привід, а інше - примус.
Tuple<string, string> tuple = name;
string[] strings = name;
Після цих перетворень кортеж і ім'я рівні, але рядки не рівні жодному з них. Ви можете трохи покращити ситуацію (або дещо заплутати), застосувавши Equals () та оператор == () у класі Name, щоб порівняти Name та рядок []. Ці оператори “виправлять” проблему порівняння, але у вас все одно будуть два окремі екземпляри; будь-яка зміна рядків не відображатиметься в назві чи кортежі, тоді як зміни в одному з імен або кортежі відображатимуться в імені та кортежі, але не в рядках.
Незважаючи на те, що наведений вище приклад мав проілюструвати деякі відмінності між приведенням та примусом, він також служить прекрасним прикладом того, чому ви повинні бути надзвичайно обережними щодо використання операторів перетворення з посилальними типами в C #.