Widziałem, jak oba terminy były używane prawie zamiennie w różnych objaśnieniach online, a większość podręczników, z którymi się zapoznałem, również nie jest do końca jasna co do tego rozróżnienia.
Czy jest może jasny i prosty sposób wyjaśnienia różnicy, którą znacie?
Konwersja typu (czasami znana również jako rzutowanie typu )
Aby użyć wartości jednego typu w kontekście, który oczekuje innego.
Rzucanie typu nonconverting (czasami nazywane kalamburem )
Zmiana, która nie zmienia podstawowych bitów.
Przymus
Proces, za pomocą którego kompilator automatycznie konwertuje wartość jednego typu na wartość innego typu, gdy ten drugi typ jest wymagany przez otaczający kontekst.
Odpowiedzi:
Konwersja typu :
Zatem przymus jest niejawny, rzutowanie jest jawne, a konwersja jest dowolną z nich.
Kilka przykładów (z tego samego źródła ):
Przymus (niejawny):
double d; int i; if (d > i) d = i;
Obsada (jawna):
double da = 3.3; double db = 3.3; double dc = 3.4; int result = (int)da + (int)db + (int)dc; //result == 9
źródło
Jak zauważyłeś, zastosowania są różne.
Moje osobiste zwyczaje to:
„Rzutowanie” to użycie operatora rzutowania . Operator rzutowania instruuje kompilator, że (1) nie jest znane to wyrażenie podanego typu, ale obiecuję, że wartość będzie tego typu w czasie wykonywania; kompilator ma traktować wyrażenie jako należące do danego typu, a środowisko wykonawcze zwróci błąd, jeśli tak nie jest, lub (2) wyrażenie jest całkowicie innego typu, ale istnieje dobrze znany sposób kojarzenia instancji typu wyrażenia z wystąpieniami typu rzutowanego. Kompilator ma wygenerować kod, który wykonuje konwersję. Uważny czytelnik zauważy, że są to przeciwieństwa, co moim zdaniem jest zgrabną sztuczką.
„Konwersja” to operacja, w wyniku której wartość jednego typu jest traktowana jako wartość innego typu - zwykle innego typu, chociaż technicznie rzecz biorąc „konwersja tożsamości” jest nadal konwersją. Konwersją może być „zmiana reprezentacji”, np. Int na double, lub „zachowanie reprezentacji”, jak ciąg na obiekt. Konwersje mogą być „niejawne”, które nie wymagają rzutowania, lub „jawne”, które wymagają rzutowania.
„Przymus” to niejawna konwersja zmieniająca reprezentację.
źródło
Data.Coerce.coerce :: Coercible a b => a -> b
działa dla typów, które mają taką samą reprezentację;Unsafe.Coerce.unsafeCoerce :: a -> b
działa na dowolne dwa typy (i sprawi, że demony wyjdą z twojego nosa, jeśli użyjesz go źle).Rzutowanie to proces, w którym traktujesz typ obiektu jako inny typ. Wymuszanie polega na konwersji jednego obiektu na inny.
Zauważ, że w poprzednim procesie nie ma konwersji, masz typ, który chciałbyś traktować jako inny, na przykład masz 3 różne obiekty dziedziczące po typie podstawowym i masz metodę, która to przejmie typ podstawowy, w dowolnym momencie, jeśli znasz konkretny typ potomny, możesz go CASTOWAĆ do tego, czym jest i użyć wszystkich określonych metod i właściwości tego obiektu, a to nie utworzy nowej instancji obiektu.
Z drugiej strony, wymuszanie implikuje utworzenie w pamięci nowego obiektu nowego typu, a następnie oryginalny typ zostałby skopiowany do nowego, pozostawiając oba obiekty w pamięci (do momentu, gdy Garbage Collectors zabierze jeden lub oba) .
Jako przykład rozważ następujący kod:
class baseClass {} class childClass : baseClass {} class otherClass {} public void doSomethingWithBase(baseClass item) {} public void mainMethod() { var obj1 = new baseClass(); var obj2 = new childClass(); var obj3 = new otherClass(); doSomethingWithBase(obj1); //not a problem, obj1 is already of type baseClass doSomethingWithBase(obj2); //not a problem, obj2 is implicitly casted to baseClass doSomethingWithBase(obj3); //won't compile without additional code }
baseClass
baseClass
otherClass
dobaseClass
, która będzie obejmować utworzenie nowego obiektu typu baseClass i wypełnienie go przez skopiowanie danych z obj3.Dobrym przykładem jest klasa Convert C #, w której udostępnia niestandardowy kod do konwersji między różnymi typami.
źródło
Rzutowanie zachowuje typ obiektów. Przymus nie.
Wymuszenie przyjmuje wartość typu, który NIE jest zgodny z przypisaniem i konwertuje na typ, który jest zgodny z przypisaniem. Tutaj wykonuję wymuszenie, ponieważ
Int32
NIE dziedziczy zInt64
... więc NIE jest kompatybilny z przypisaniem. To jest coraz większy przymus (brak utraty danych). Rozszerzający się przymus to niejawna konwersja . Przymus dokonuje konwersji.void Main() { System.Int32 a = 100; System.Int64 b = a; b.GetType();//The type is System.Int64. }
Rzutowanie pozwala traktować typ tak, jakby był innego typu, jednocześnie zachowując typ .
void Main() { Derived d = new Derived(); Base bb = d; //b.N();//INVALID. Calls to the type Derived are not possible because bb is of type Base bb.GetType();//The type is Derived. bb is still of type Derived despite not being able to call members of Test } class Base { public void M() {} } class Derived: Base { public void N() {} }
Źródło: The Common Language Infrastructure Annotated Standard Jamesa S. Millera
Co dziwne, dokumentacja Microsoftu dotycząca Casting nie jest zgodna z definicją Casting w specyfikacji ecma-335.
... To brzmi jak przymus, a nie rzucanie.
Na przykład,
object o = 1; int i = (int)o;//Explicit conversions require a cast operator i.GetType();//The type has been explicitly converted to System.Int32. Object type is not preserved. This meets the definition of Coercion not casting.
Kto wie? Może Microsoft sprawdza, czy ktoś to czyta.
źródło
Poniżej znajduje się post z następującego artykułu :
Różnica między przymusem a rzucaniem jest często pomijana. Rozumiem dlaczego; wiele języków ma tę samą (lub podobną) składnię i terminologię dla obu operacji. Niektóre języki mogą nawet odnosić się do dowolnej konwersji jako „rzutowanie”, ale poniższe wyjaśnienie odnosi się do pojęć w CTS.
Jeśli próbujesz przypisać wartość pewnego typu do lokalizacji innego typu, możesz wygenerować wartość nowego typu, która ma podobne znaczenie do oryginału. To jest przymus. Wymuszenie pozwala na użycie nowego typu poprzez utworzenie nowej wartości, która w pewien sposób przypomina oryginał. Niektóre wymuszenia mogą odrzucać dane (np. Konwertowanie int 0x12345678 na krótkie 0x5678), podczas gdy inne nie (np. Konwertowanie int 0x00000008 na krótkie 0x0008 lub długie 0x0000000000000008).
Przypomnij sobie, że wartości mogą mieć wiele typów. Jeśli Twoja sytuacja jest nieco inna i chcesz wybrać tylko inny typ wartości, narzędziem do tego zadania jest rzutowanie. Rzutowanie po prostu wskazuje, że chcesz operować na określonym typie, który zawiera wartość.
Różnica na poziomie kodu różni się od C # do IL. W C # zarówno rzutowanie, jak i przymus wyglądają dość podobnie:
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; }
Na poziomie IL są zupełnie inne:
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
Jeśli chodzi o poziom logiczny, istnieje kilka ważnych różnic. Najważniejsze do zapamiętania jest to, że przymus tworzy nową wartość, podczas gdy rzucanie nie. Tożsamość wartości pierwotnej i wartości po rzutowaniu są takie same, podczas gdy tożsamość wartości wymuszonej różni się od wartości pierwotnej; koersion tworzy nową, odrębną instancję, podczas gdy rzutowanie nie. Konsekwencją jest to, że wynik rzutowania i oryginał zawsze będą równoważne (zarówno pod względem tożsamości, jak i równości), ale wymuszona wartość może być równa oryginałowi lub nie, i nigdy nie ma takiej samej tożsamości.
W powyższych przykładach łatwo jest zobaczyć konsekwencje przymusu, ponieważ typy liczbowe są zawsze kopiowane według wartości. Podczas pracy z typami referencyjnymi sprawy stają się nieco trudniejsze.
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 }; } }
W poniższym przykładzie jedna konwersja to rzut, a druga to przymus.
Tuple<string, string> tuple = name; string[] strings = name;
Po tych konwersjach krotka i nazwa są równe, ale ciągi znaków nie są równe żadnemu z nich. Możesz nieco poprawić sytuację (lub nieco bardziej zagmatwać), implementując Equals () i operator == () w klasie Name w celu porównania nazwy i ciągu []. Te operatory „rozwiązałyby” problem z porównaniem, ale nadal istniałyby dwie oddzielne instancje; jakakolwiek modyfikacja łańcuchów nie byłaby odzwierciedlona w nazwie lub krotce, podczas gdy zmiany w nazwie lub krotce byłyby odzwierciedlone w nazwie i krotce, ale nie w łańcuchach.
Chociaż powyższy przykład miał na celu zilustrowanie pewnych różnic między rzutowaniem a wymuszeniem, służy on również jako doskonały przykład tego, dlaczego należy zachować szczególną ostrożność podczas używania operatorów konwersji z typami referencyjnymi w języku C #.
źródło
Ze standardu CLI :
źródło
Według Wikipedii
Różnica między rzutowaniem typów a koercją typów jest następująca:
TYPE CASTING | TYPE COERCION | 1. Explicit i.e., done by user | 1. Implicit i.e., done by the compiler | 2. Types: | 2. Type: Static (done at compile time) | Widening (conversion to higher data | type) Dynamic (done at run time) | Narrowing (conversion to lower data | type) | 3. Casting never changes the | 3. Coercion can result in representation the actual type of object | as well as type change. nor representation. |
Uwaga : przesyłanie nie jest konwersją. To tylko proces, w którym traktujemy typ obiektu jako inny typ. Dlatego rzeczywisty typ obiektu, jak również reprezentacja, nie są zmieniane podczas rzutowania.
Zgadzam się ze słowami @ PedroC88:
źródło