2010-03-21 24 views
8

Cả hai đều là đại biểu và có chữ ký giống nhau, nhưng tôi không thể sử dụng Hành động làm ThreadStart.Tại sao tôi không thể sử dụng/bỏ một Hành động cho/đến một ThreadStart?

Tại sao?

Action doIt; 
doIt =() => MyMethod("test"); 
Thread t; 

t = new Thread(doIt); 
t.Start(); 

nhưng điều này dường như làm việc:

Thread t; 

t = new Thread(() => MyMethod("test")); 
t.Start(); 
+1

Có vẻ như đó là một diễn viên tiềm ẩn từ đại biểu đến Hành động chứ không phải theo cách khác. Bạn có thể thử tạo một hoặc bạn chỉ có thể thay đổi 't = new Thread (doIt);' thành 't = new Thread (doIt.Invoke);' cho cùng một kết quả. –

Trả lời

20

Như những người khác đã lưu ý, vấn đề là các loại đại biểu không phải là "kết cấu". Nghĩa là, họ không có sự tương đương dựa trên "cấu trúc" của họ.

Bây giờ, điều này được cho là một điều tốt cho một số loại.Nếu bạn có

struct MyRectangle { int x; int y; int width; int height; ... } 

struct YourRectangle { int x1; int y1; int x2; int y2; ... } 

rõ ràng nó sẽ là một sai lầm nếu cho phép các trường hợp MyRectangle được gán cho các biến của YourRectangle, chỉ vì cả hai đều bao gồm bốn ints. Các ngữ nghĩa của các int là khác nhau và do đó các loại không tương đương.

Điều tương tự cũng đúng với các đại biểu. Bạn có thể có

delegate int Pure(string x); 
delegate int Func(string x); 

trong đó chức năng "thuần" là một không có tác dụng phụ và cùng một đầu ra cho cùng một đầu vào. Vì mỗi Pure là một Func hợp lý, nhưng mỗi Func không nhất thiết phải là Pure, không nên có sự kết cấu cấu trúc giữa chúng.

Trong thực tế, hệ thống kiểu không hỗ trợ các khái niệm như "hàm thuần túy" rất tốt. Và trong thực tế, phần lớn các nỗ lực để chuyển đổi giữa các loại đại biểu là hoàn toàn an toàn: chuyển đổi từ một Func<int, bool> thành Predicate<int> và cứ tiếp tục như vậy.

Vì vậy, có hai thứ, một người nhìn về phía sau và một người đang nhìn về phía trước. Ngược lại: nếu chúng tôi phải làm lại tất cả, tôi nghĩ các đại biểu có lẽ sẽ được nhập cấu trúc trong CLI. Bạn không phải lúc nào cũng biết những tính năng nào sẽ hữu ích khi bạn thiết kế một khung công tác hoàn toàn mới, và các loại đại biểu phi cấu trúc trước đây hóa ra lại không hữu ích như dự đoán. Tiền đạo: Tôi hy vọng sẽ thấy nhiều tính năng hơn trong các phiên bản sau của CLR cho phép nhập nhiều cấu trúc hơn. Ví dụ, tính năng "no pia" trong C# 4 là tạo ra hai kiểu ngữ nghĩa và cấu trúc giống nhau, nhưng được định nghĩa trong các assembly khác nhau, hợp nhất một cách hợp lý về mặt cấu trúc.

+0

Bất kỳ đại biểu cơ hội nào cũng có thể trở thành cấu trúc, i.o.w. điều đó sẽ phá vỡ bất cứ điều gì? –

+0

Chắc chắn, mọi thứ có thể bị phá vỡ. Đó sẽ là một sự thay đổi lớn lao. * Bất kỳ * thay đổi nào đối với các quy tắc chuyển đổi loại gây ra thay đổi đột ngột. Nếu bạn làm cho những gì được sử dụng là hợp pháp bất hợp pháp, rõ ràng đó là một sự thay đổi phá vỡ. Nếu bạn làm một cái gì đó mà được sử dụng để được pháp lý bất hợp pháp, sau đó bạn có thể giới thiệu một sự mơ hồ trong độ phân giải quá tải. –

+0

Người ta có thể đi một chặng đường dài bằng cách đơn giản thêm quá tải chấp nhận một biến thể thích hợp của 'Hành động' /' Func' cho tất cả các API nhận các đại biểu. Đó là một * rất nhiều * quá tải mặc dù ... Là một lợi ích bổ sung, chữ ký mong đợi là ngay lập tức rõ ràng thông qua IntelliSense, mà không có bất kỳ thêm Visual Studio tweaks. –

1

Hãy thử:

t = new Thread(new ThreadStart(doIt)); 

hoặc

t = new Thread(()=>MyMethod("test")); 

Bạn đang cố gắng để vượt qua một đối số kiểu "hành động" cho một hàm tạo có một ThreadStart. Không có một chuyển đổi ngầm định được xác định, do đó bạn cần phải gọi thủ công hàm tạo ThreadStart.

3

Các đại biểu có cùng chữ ký không giống nhau trong mắt của CLR - chúng hoàn toàn khác nhau.

1

Vì bắt đầu chuỗi là loại đại biểu riêng biệt và không thể chuyển đổi Action thành ThreadStart. làm việc

trường hợp này vì đây lambda của bạn được xử lý bởi trình biên dịch như ThreadStart:

Thread t; 

t = new Thread(() => MyMethod("test")); 
t.Start(); 
3

Các hình thức cơ bản của lỗi này là:

delegate void d1(); 
delegate void d2(); 
d1 a; 
d2 b; 
b = a; 

Error Cannot implicitly convert type 'ConsoleDemo1.d1' to 'ConsoleDemo1.d2' 

Vì vậy, bạn có thể 'giải quyết' bạn mẫu đầu tiên bằng cách sử dụng đúng loại đại biểu:

//Action doIt; 
ThreadStart doIt; 
doIt =() => MyMethod("test"); 
Thread t = new Thread(doIt); 
+0

Đó không phải những gì tôi thấy. Tôi nhìn thấy một diễn viên tiềm ẩn từ một đại biểu đến một hành động nhưng không có diễn viên tiềm ẩn từ một hành động cho một đại biểu. –

+0

@Spencer, Hành động là một đại biểu (cụ thể). –

4

Hành vi bạn nhận thấy là bởi vì Action là một loại và 'lambda' trong ví dụ làm việc thứ hai của bạn, không phải là.

Có thể cho rằng, chúng giống nhau, nhưng bạn đang sử dụng loại khuôn khổ cụ thể khi sử dụng Action, trong khi trình biên dịch nhập chữ ký của lambda.

EDIT: để được rõ ràng:

hành động là một loại đại biểu, đó là not một ủy nhiệm ThreadStart, vì vậy khi bạn có thể gán biểu thức lambda cho cả hai, bạn không thể sử dụng cả hai để bắt đầu một chủ đề.

Trong ví dụ thứ hai, bạn đang cho trình biên dịch cơ hội suy ra loại đại biểu và, không có gì ngạc nhiên, nó có thể gán biểu thức lamda cho kiểu ThreadStart.

+0

Các lambda không phải là một loại, nó là một giá trị gán cho các loại hành động/ThreadStart. Lambda không liên quan đến lỗi. –

+0

Hoàn toàn là, Henk. Trong ví dụ sau, lambda được suy ra là một ThreadStart, trong khi ở trước đây, nó được đánh một cách rõ ràng là một Action. Cái sau tương thích với một trong các hàm tạo lớp Thread, trong khi cái cũ thì không. – Chris

+0

@Henk,? bạn có đồng ý với tôi hay ngụ ý rằng tôi đã nói khác không? không chắc chắn .... –

3

Tôi tin rằng điều này sẽ hoạt động?

Action doIt; 
doIt =() => MyMethod("test"); 
Thread t; 

t = new Thread(doIt.Invoke); 
t.Start(); 
+0

có tác dụng này. NHƯNG! TẠI SAO? Và tại sao tôi có thể gọi Invoke bằng dấu ngoặc? Vì khi nào phương thức C# có thể được gọi theo kiểu VB: D? – Rookian

+0

@Rookian: 'doIt.Invoke' được tự động gói trong một đại biểu' ThreadStart'. Nó không thực sự gọi 'Invoke'. Đây là hành vi tương tự bạn thấy với bất kỳ phương thức nào khác trả về void và không có tham số. –

+0

@Rookian: Trước tiên, tôi tưởng tượng không có diễn viên tiềm ẩn nào từ Hành động tới đại biểu bởi vì bạn sẽ được truyền sang loại mang dữ liệu ít hơn. Không có gì ngăn cản bạn viết một dàn diễn viên tiềm ẩn, nhưng các nhà thiết kế .Net không được cảm thấy như là một ý tưởng hay để đưa vào tiêu chuẩn này. Thứ hai, bạn không gọi hàm khi bạn không bao gồm dấu ngoặc đơn, bạn đang chuyển nhóm phương thức có một phép ẩn ngầm tới đại biểu ThreadStart. –

2

Tôi nghĩ rằng sự nói dài giòng sau đây trong phần 26.3.1 của C# Language Specification là quan trọng:

Tương tự như một anonymous-phương pháp thể hiện, một lambda-biểu được phân loại là một giá trị với quy tắc chuyển đổi đặc biệt. Giá trị không có loại nhưng có thể được chuyển đổi hoàn toàn thành loại đại biểu tương thích . Cụ thể, một loại đại biểu D là tương thích với một lambda-biểu L cung cấp:
[Danh sách elided]

nào ngăn chặn mã như thế này từ biên soạn:

var doIt =() => MyMethod("test"); // CS0815 

Dẫn đến:

Action<object> doIt = (o) => MyMethod("test"); 
t = new Thread((ParameterizedThreadStart)doIt); // CS0030 

Bị đánh bại bởi trình biên dịch không cho phép chuyển đổi từ loại đại biểu này sang loại người khác, ngay cả khi chữ ký của họ tương thích. Buộc:

ParameterizedThreadStart doIt = (o) => MyMethod("test"); 
t = new Thread(doIt); 

Biên dịch nào không gặp sự cố.


không liên quan tính năng tiền thưởng: kiểm tra khả năng sử dụng từ rất sớm của Apple Mac đầu tiên phát hiện ra một vấn đề với phiên bản đầu tiên của nút OK trên hộp thoại mà sử dụng một phông chữ sans-serif. Người dùng thường nhấp vào Hủy thay vào đó, với một số nỗi đau có thể nhìn thấy. Sau nhiều cuộc phỏng vấn, một người dùng cuối cùng đã thừa nhận vấn đề: "Tôi ghét nó khi một máy tính gọi tôi là Dolt!"

Vâng, trình biên dịch gọi bạn là dolt có lẽ không liên quan :)

Các vấn đề liên quan