Sự khác biệt giữa ép và ép là gì?


85

Tôi đã thấy cả hai thuật ngữ được sử dụng gần như thay thế cho nhau trong các giải thích trực tuyến khác nhau và hầu hết các sách văn bản mà tôi đã tham khảo cũng không hoàn toàn rõ ràng về sự phân biệt.

Có lẽ có cách nào rõ ràng và đơn giản để giải thích sự khác biệt mà các bạn biết không?

Chuyển đổi kiểu (đôi khi còn được gọi là kiểu truyền )

Để sử dụng giá trị của một loại trong ngữ cảnh mong đợi một loại khác.

Kiểu ép kiểu không chuyển đổi (đôi khi được gọi là kiểu chơi chữ )

Một thay đổi không làm thay đổi các bit cơ bản.

Ép buộc

Quá trình trình biên dịch tự động chuyển đổi giá trị của một kiểu thành giá trị của kiểu khác khi kiểu thứ hai đó được ngữ cảnh xung quanh yêu cầu.


Câu trả lời:


114

Loại chuyển đổi :

Từ chuyển đổi đề cập đến việc thay đổi ngầm hoặc rõ ràng một giá trị từ kiểu dữ liệu này sang kiểu dữ liệu khác, ví dụ: số nguyên 16 bit thành số nguyên 32 bit.

Từ ép buộc được sử dụng để biểu thị một sự chuyển đổi ngầm.

Từ ép kiểu thường đề cập đến chuyển đổi kiểu rõ ràng (trái ngược với chuyển đổi ngầm định), bất kể đây là diễn giải lại mẫu bit hay chuyển đổi thực.

Vì vậy, ép buộc là ngầm, ép buộc là rõ ràng và chuyển đổi là bất kỳ trong số đó.


Một vài ví dụ (từ cùng một nguồn ):

Cưỡng chế (ngầm):

double  d;
int     i;
if (d > i)      d = i;

Truyền (rõ ràng):

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9

điều này có làm cho "ép buộc ngầm" thừa không? ghi chú ở đây sử dụng cả "sự ép buộc ngầm" và "sự ép buộc rõ ràng"
Dave Cousineau

1
Chuyển đổi ngầm chỉ có thể được thực hiện khi bạn không mất độ chính xác hoặc có ý nghĩa (Ví dụ: Int -> double). Trong hầu hết các ngôn ngữ hiện đại, bạn không thể thực hiện double-> int vì bạn sẽ mất độ chính xác. Với kiểu cưỡng chế, đó không phải là "vấn đề".
Maxime Rouiller

Câu trả lời này không phù hợp với các thông số kỹ thuật như được định nghĩa trong ecma 335 cho CIL. Tôi đã đưa ra định nghĩa đặc điểm kỹ thuật với các ví dụ trong câu trả lời của mình.
P.Brian.Mackey

24

Các tập quán khác nhau, như bạn lưu ý.

Tập quán cá nhân của tôi là:

  • "Cast" là cách sử dụng toán tử ép kiểu . Một toán tử ép kiểu chỉ thị cho trình biên dịch rằng (1) biểu thức này không được biết là thuộc kiểu đã cho, nhưng tôi hứa với bạn rằng giá trị sẽ thuộc kiểu đó trong thời gian chạy; trình biên dịch phải coi biểu thức là một kiểu đã cho và thời gian chạy sẽ tạo ra lỗi nếu không phải, hoặc (2) biểu thức thuộc một kiểu hoàn toàn khác, nhưng có một cách nổi tiếng để liên kết các thể hiện kiểu của biểu thức với các thể hiện của kiểu truyền thành. Trình biên dịch được hướng dẫn để tạo mã thực hiện chuyển đổi. Người đọc chú ý sẽ lưu ý rằng đây là những điều đối lập, mà tôi nghĩ đó là một mẹo nhỏ.

  • "Chuyển đổi" là một hoạt động trong đó giá trị của một loại được coi là giá trị của một loại khác - thường là một loại khác, mặc dù về mặt kỹ thuật, "chuyển đổi danh tính" vẫn là một chuyển đổi. Việc chuyển đổi có thể là "thay đổi biểu diễn", như int thành double hoặc có thể là "bảo toàn biểu diễn" như chuỗi thành đối tượng. Các lượt chuyển đổi có thể là "ẩn", không yêu cầu diễn viên hoặc "rõ ràng", không yêu cầu diễn viên.

  • "Sự ép buộc" là một chuyển đổi ngầm định thay đổi đại diện.


1
Tôi nghĩ câu đầu tiên của câu trả lời này là điều quan trọng nhất. Các ngôn ngữ khác nhau sử dụng các thuật ngữ này để có nghĩa là những thứ hoàn toàn khác nhau. Trong Haskell, ví dụ, một "ép buộc" không bao giờ thay đổi đại diện; một sự cưỡng chế an toàn, Data.Coerce.coerce :: Coercible a b => a -> bhoạt động đối với các loại được chứng minh là có cùng một đại diện; Unsafe.Coerce.unsafeCoerce :: a -> bcó tác dụng với bất kỳ hai loại nào (và sẽ khiến ma quỷ chui ra khỏi mũi nếu bạn sử dụng sai).
dfeuer

@dfeuer điểm dữ liệu thú vị, cảm ơn! Tôi lưu ý rằng thông số kỹ thuật C # không định nghĩa "cưỡng chế"; đề nghị của tôi chỉ là những gì cá nhân tôi muốn nói. Cho rằng thuật ngữ này có vẻ được định nghĩa kém, tôi thường tránh nó.
Eric Lippert

8

Casting là quá trình bạn coi một kiểu đối tượng như một kiểu khác, ép buộc là chuyển đổi đối tượng này sang đối tượng khác.

Lưu ý rằng trong quá trình trước đây không có chuyển đổi liên quan, bạn có một kiểu mà bạn muốn coi là một kiểu khác, chẳng hạn như bạn có 3 đối tượng khác nhau kế thừa từ một kiểu cơ sở và bạn có một phương thức sẽ lấy base type, tại bất kỳ thời điểm nào, nếu bạn biết kiểu con cụ thể, bạn có thể CAST nó thành kiểu gì và sử dụng tất cả các phương thức và thuộc tính cụ thể của đối tượng đó và điều đó sẽ không tạo ra một phiên bản mới của đối tượng.

Mặt khác, ép buộc ngụ ý việc tạo một đối tượng mới trong bộ nhớ của kiểu mới và sau đó kiểu gốc sẽ được sao chép sang kiểu mới, để lại cả hai đối tượng trong bộ nhớ (cho đến khi Bộ thu gom rác lấy đi hoặc cả hai) .

Ví dụ, hãy xem xét đoạn mã sau:

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
}
  • obj1 được thông qua mà không có bất kỳ ép buộc hoặc ép buộc (chuyển đổi) nào vì nó đã thuộc cùng một loại baseClass
  • obj2 được truyền ngầm sang cơ sở, có nghĩa là không có tạo đối tượng mới vì obj2 đã có thể baseClass
  • obj3 cần được chuyển đổi bằng cách nào đó thành base, bạn sẽ cần cung cấp phương thức của riêng mình để chuyển từ otherClasssang baseClass, điều này sẽ liên quan đến việc tạo một đối tượng mới kiểu baseClass và điền nó bằng cách sao chép dữ liệu từ obj3.

Một ví dụ điển hình là lớp Convert C # nơi nó cung cấp mã tùy chỉnh để chuyển đổi giữa các loại khác nhau.


3
Một ví dụ sẽ giúp làm rõ sự khác biệt mà bạn đang cố gắng tạo ra.
Oliver Charlesworth

2

Đúc bảo toàn loại đối tượng. Sự ép buộc không.

Sự ép buộc là lấy giá trị của một kiểu KHÔNG tương thích với phép gán và chuyển đổi sang một kiểu tương thích với phép gán. Ở đây tôi thực hiện một sự ép buộc vì Int32KHÔNG kế thừa từ Int64... nên nó KHÔNG tương thích với phép gán. Đây là một cuộc cưỡng chế mở rộng (không có dữ liệu bị mất). Một sự ép buộc mở rộng còn được gọi là một chuyển đổi ngầm . Một sự ép buộc thực hiện một chuyển đổi.

void Main()
{
    System.Int32 a = 100;
    System.Int64 b = a;
    b.GetType();//The type is System.Int64.  
}

Đúc cho phép bạn xử lý một loại như thể nó là một loại khác trong khi vẫn giữ nguyên loại .

    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() {}
    }

Nguồn: Tiêu chuẩn Chú giải Cơ sở hạ tầng Ngôn ngữ Chung của James S. Miller

Điều kỳ lạ là tài liệu của Microsoft về Truyền không phù hợp với định nghĩa thông số kỹ thuật ecma-335 về Truyền.

Chuyển đổi rõ ràng (truyền): Chuyển đổi rõ ràng yêu cầu toán tử truyền. Truyền là bắt buộc khi thông tin có thể bị mất trong chuyển đổi hoặc khi chuyển đổi có thể không thành công vì các lý do khác. Các ví dụ điển hình bao gồm chuyển đổi số thành một kiểu có độ chính xác thấp hơn hoặc phạm vi nhỏ hơn và chuyển đổi một cá thể lớp cơ sở thành một lớp dẫn xuất.

... Điều này có vẻ như Coercions không Casting.

Ví dụ,

  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.

Ai biết? Có lẽ Microsoft đang kiểm tra xem có ai đọc nội dung này không.


1

Dưới đây là một bài đăng từ bài báo sau :

Sự khác biệt giữa cưỡng chế và đúc thường bị bỏ qua. Tôi có thể hiểu tại sao; nhiều ngôn ngữ có cú pháp và thuật ngữ giống nhau (hoặc tương tự) cho cả hai hoạt động. Một số ngôn ngữ thậm chí có thể gọi bất kỳ chuyển đổi nào là “truyền”, nhưng giải thích sau đây đề cập đến các khái niệm trong CTS.

Nếu bạn đang cố gắng gán một giá trị của một số loại cho một vị trí của một loại khác, bạn có thể tạo một giá trị của loại mới có ý nghĩa tương tự với giá trị ban đầu. Đây là sự ép buộc. Coercion cho phép bạn sử dụng kiểu mới bằng cách tạo một giá trị mới giống với giá trị ban đầu theo một cách nào đó. Một số cưỡng chế có thể loại bỏ dữ liệu (ví dụ: chuyển đổi int 0x12345678 thành 0x5678 ngắn), trong khi các lệnh khác có thể không (ví dụ: chuyển đổi int 0x00000008 thành 0x0008 ngắn hoặc 0x0000000000000008 dài).

Nhớ lại rằng các giá trị có thể có nhiều loại. Nếu tình huống của bạn hơi khác một chút và bạn chỉ muốn chọn một loại khác trong các loại giá trị, thì truyền là công cụ cho công việc. Truyền chỉ đơn giản cho biết rằng bạn muốn hoạt động trên một kiểu cụ thể mà một giá trị bao gồm.

Sự khác biệt ở cấp độ mã thay đổi từ C # đến IL. Trong C #, cả ép kiểu và ép buộc trông khá giống nhau:

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;
}

Ở cấp độ IL, chúng khá khác nhau:

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

Đối với mức độ logic, có một số khác biệt quan trọng. Điều quan trọng nhất cần nhớ là ép buộc tạo ra một giá trị mới, trong khi ép buộc thì không. Bản sắc của giá trị ban đầu và giá trị sau khi đúc giống nhau, trong khi bản sắc của giá trị bị cưỡng chế khác với giá trị ban đầu; coersion tạo ra một thể hiện mới, riêng biệt, trong khi ép kiểu thì không. Một hệ quả tất yếu là kết quả đúc và bản gốc sẽ luôn tương đương (cả về danh tính và bình đẳng), nhưng một giá trị cưỡng chế có thể bằng hoặc không bằng giá trị ban đầu và không bao giờ có chung danh tính ban đầu.

Thật dễ dàng để thấy ý nghĩa của việc ép buộc trong các ví dụ trên, vì các kiểu số luôn được sao chép theo giá trị. Mọi thứ trở nên phức tạp hơn một chút khi bạn làm việc với các loại tham chiếu.

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 };
    }
}

Trong ví dụ bên dưới, một chuyển đổi là ép kiểu, trong khi chuyển đổi kia là cưỡng chế.

Tuple<string, string> tuple = name;
string[] strings = name;

Sau những chuyển đổi này, tuple và tên bằng nhau, nhưng các chuỗi không bằng một trong hai. Bạn có thể làm cho tình hình tốt hơn một chút (hoặc khó hiểu hơn một chút) bằng cách triển khai Equals () và toán tử == () trên lớp Name để so sánh Tên và chuỗi []. Các toán tử này sẽ "khắc phục" vấn đề so sánh, nhưng bạn vẫn sẽ có hai trường hợp riêng biệt; bất kỳ sửa đổi nào đối với chuỗi sẽ không được phản ánh trong tên hoặc tuple, trong khi các thay đổi đối với một trong tên hoặc tuple sẽ được phản ánh trong tên và tuple, nhưng không được phản ánh trong chuỗi.

Mặc dù ví dụ trên nhằm minh họa một số khác biệt giữa ép kiểu và ép buộc, nhưng nó cũng là một ví dụ tuyệt vời về lý do tại sao bạn nên cực kỳ thận trọng khi sử dụng các toán tử chuyển đổi với các kiểu tham chiếu trong C #.


1

Từ tiêu chuẩn CLI :

I.8.3.2 Cưỡng chế

Đôi khi người ta mong muốn nhận một giá trị của một loại không thể gán-cho một vị trí và chuyển đổi giá trị thành một loại có thể gán-cho loại vị trí. Điều này được thực hiện thông qua cưỡng chế giá trị. Sự ép buộc nhận một giá trị của một kiểu cụ thể và một kiểu mong muốn và cố gắng tạo ra một giá trị của kiểu mong muốn có ý nghĩa tương đương với giá trị ban đầu. Sự ép buộc có thể dẫn đến thay đổi cách biểu diễn cũng như thay đổi kiểu; do đó việc ép buộc không nhất thiết phải bảo toàn danh tính đối tượng.

Có hai loại ép buộc: mở rộng , không bao giờ mất thông tin và thu hẹp , trong đó thông tin có thể bị mất. Ví dụ về sự ép buộc mở rộng sẽ ép buộc một giá trị là số nguyên có dấu 32 bit thành giá trị là số nguyên có dấu 64 bit. Một ví dụ về cưỡng chế thu hẹp là ngược lại: ép buộc số nguyên có dấu 64 bit thành số nguyên có dấu 32 bit. Các ngôn ngữ lập trình thường triển khai các cưỡng chế mở rộng dưới dạng chuyển đổi ngầm , trong khi các cưỡng chế thu hẹp thường yêu cầu một chuyển đổi rõ ràng .

Một số cưỡng chế được tích hợp trực tiếp vào các hoạt động VES trên các kiểu tích hợp sẵn (xem §I.12.1). Tất cả các cưỡng chế khác sẽ được yêu cầu rõ ràng. Đối với các kiểu tích hợp sẵn, CTS cung cấp các hoạt động để thực hiện các cưỡng chế mở rộng mà không cần kiểm tra thời gian chạy và thu hẹp các cưỡng chế với kiểm tra thời gian chạy hoặc cắt bớt, theo ngữ nghĩa hoạt động.

I.8.3.3 Đúc

Vì một giá trị có thể có nhiều loại, việc sử dụng giá trị cần phải xác định rõ ràng loại nào của nó đang được sử dụng. Vì các giá trị được đọc từ các vị trí được nhập, loại giá trị được sử dụng là loại vị trí mà từ đó giá trị được đọc. Nếu một loại khác nhau được sử dụng, giá trị được đúc với một trong các loại khác của nó. Truyền thường là một hoạt động thời gian biên dịch, nhưng nếu trình biên dịch không thể biết một cách tĩnh rằng giá trị thuộc kiểu đích, thì việc kiểm tra ép thời gian chạy sẽ được thực hiện. Không giống như ép buộc, ép kiểu không bao giờ thay đổi kiểu thực tế của một đối tượng cũng như không thay đổi cách biểu diễn. Đúc bảo tồn danh tính của các đối tượng.

Ví dụ: có thể cần kiểm tra thời gian chạy khi truyền một giá trị được đọc từ một vị trí được nhập là giữ giá trị của một giao diện cụ thể. Vì giao diện là một mô tả không đầy đủ về giá trị, việc truyền giá trị đó thành một kiểu giao diện khác thường sẽ dẫn đến việc kiểm tra truyền trong thời gian chạy.


1

Theo Wikipedia,

Trong khoa học máy tính, chuyển đổi kiểu, ép kiểu, ép kiểu và tung kiểu là những cách khác nhau để thay đổi một biểu thức từ kiểu dữ liệu này sang kiểu dữ liệu khác.

Sự khác biệt giữa kiểu đúc và kiểu ép như sau:

           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.            |

Lưu ý : Truyền không phải là chuyển đổi. Nó chỉ là quá trình mà chúng ta coi một kiểu đối tượng như một kiểu khác. Do đó, kiểu thực tế của đối tượng, cũng như hình biểu diễn, không bị thay đổi trong quá trình ép.

Tôi đồng ý với lời của @ PedroC88:

Mặt khác, ép buộc ngụ ý việc tạo một đối tượng mới trong bộ nhớ của kiểu mới và sau đó kiểu gốc sẽ được sao chép sang kiểu mới, để lại cả hai đối tượng trong bộ nhớ (cho đến khi Bộ thu gom rác lấy đi hoặc cả hai) .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.