2012-01-11 23 views
8

Tôi có một phương pháp tĩnh:Delegate.CreateDelegate sẽ không đưa ra giá trị trả về - cố tình, hoặc không hoạt động?

public class Example 
{ 
    //for demonstration purposes - just returns default(T) 
    public static T Foo<T>() { return default(T); } 
} 

Và tôi cần để có thể gọi nó bằng một tham số Type cuộc gọi đến mà có thể là nhiều, vì vậy mô hình tiêu chuẩn của tôi là tạo ra một bộ nhớ cache thread-safe của các đại biểu (sử dụng ConcurrentDictionary trong .Net 4) tự động gọi phương thức Foo<T> với chính xác T. Nếu không có bộ nhớ đệm, tuy nhiên, mã này là:

static object LateFoo(Type t) 
{ 
    //creates the delegate and invokes it in one go 
    return (Func<object>)Delegate.CreateDelegate( 
    typeof(Func<object>), 
    typeof(Example).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static). 
     MakeGenericMethod(t))(); 
} 

Đây không phải là lần đầu tiên tôi đã phải làm điều này - và trong quá khứ tôi đã sử dụng cây biểu thức để xây dựng và biên soạn một proxy để gọi phương pháp đích - để đảm bảo rằng việc chuyển đổi kiểu trả về và boxing từ đối tượng int -> (ví dụ) được xử lý một cách chính xác.

Update - ví dụ về mã biểu làm việc

static object LateFoo(Type t) 
{ 
    var method = typeof(Example) 
       .GetMethod("Foo", BindingFlags.Public | BindingFlags.Static) 
       .MakeGenericMethod(t); 
    //in practise I cache the delegate, invoking it freshly built or from the cache 
    return Expression.Lambda<Func<IField, object>>(Expression.Convert(
    Expression.Call(method), typeof(object))).Compile()(); 
} 

gì hơi buồn cười là tôi học được từ rất sớm với các biểu thức rằng một rõ ràng Convert được yêu cầu và chấp nhận nó - và thay cho câu trả lời ở đây nó bây giờ không có ý nghĩa tại sao khuôn khổ Net không tự động gắn bó tương đương trong.

End cập nhật

Tuy nhiên, lần này tôi nghĩ tôi chỉ muốn sử dụng Delegate.CreateDelegate vì nó làm cho tấn công tuyệt vời của thực tế là (từ MSDN):

Tương tự, kiểu trả về của một đại biểu là phù hợp với kiểu trả về của một phương thức nếu kiểu trả về của phương thức là hạn chế hơn kiểu trả về của đại biểu, bởi vì điều này đảm bảo rằng giá trị trả về của phương thức có thể được truyền an toàn đến kiểu trả về của đại biểu.

Bây giờ - nếu tôi vượt qua typeof(string) đến LateFoo, mọi thứ đều ổn.

Nếu, tuy nhiên, tôi vượt qua typeof(int) Tôi nhận được ArgumentException trên số CreateDelegate cuộc gọi, tin nhắn: Error binding to target method. Không có ngoại lệ bên trong hoặc thông tin thêm.

Vì vậy, có vẻ như, đối với mục đích ràng buộc phương pháp, object không được coi là hạn chế hơn int. Rõ ràng, điều này phải được thực hiện với boxing là một hoạt động khác với một loại chuyển đổi đơn giản và các loại giá trị không được coi là covariant thành object trong khuôn khổ .Net; mặc dù mối quan hệ kiểu thực tế khi chạy.

Các biên dịch C# dường như đồng ý với điều này (chỉ con đường ngắn nhất tôi có thể mô hình lỗi, bỏ qua những gì mã sẽ làm gì):

public static int Foo() 
{ 
    Func<object> f = new Func<object>(Foo); 
    return 0; 
} 

Không biên dịch bởi vì phương pháp Foo 'có kiểu trả về sai '- đưa ra các vấn đề CreateDelegate, C# chỉ đơn giản là sau.

Dường như với tôi điều đó.Net là không phù hợp trong điều trị hiệp phương sai - hoặc là một loại giá trị là object hoặc không; & nếu không, không được để lộ object làm cơ sở (mặc dù nó sẽ khó khăn hơn bao nhiêu trong cuộc sống của chúng ta). Vì nó phơi bày object làm cơ sở (hoặc là nó chỉ là ngôn ngữ thực hiện điều đó?), Sau đó theo logic một loại giá trị nên được so sánh với object (hoặc bất kỳ cách nào xung quanh bạn có nghĩa vụ phải nói nó) làm cho ràng buộc đại biểu này đúng. Nếu hiệp phương sai đó chỉ có thể đạt được thông qua một hoạt động đấm bốc; thì khung công tác phải quan tâm đến điều đó.

Tôi dám nói câu trả lời ở đây sẽ là CreateDelegate không nói rằng nó sẽ xử lý một hoạt động hộp trong hiệp phương sai vì nó chỉ sử dụng từ 'đúc'. Tôi cũng hy vọng có toàn bộ luận thuyết về chủ đề rộng hơn về các loại giá trị và hiệp phương sai đối tượng, và tôi đang hét lên về một chủ đề lâu dài và không còn tồn tại. Tôi nghĩ rằng có điều gì đó mà tôi không hiểu hoặc đã bỏ lỡ, mặc dù - vì vậy hãy khai sáng!

Nếu điều này là không thể trả lời - Tôi rất sẵn lòng xóa.

+0

@ HenkHolterman: Tôi nghĩ vậy, nhưng vẫn tiếp tục đọc và tôi nghĩ đó là một câu hỏi hay. –

+0

Tôi phải thiếu thứ gì đó vì tôi không thể biên dịch phương thức MakeDelegate 'Không thể chuyển đổi System.Delegate thành Func '? –

+0

@ Myles - bạn nói đúng - bỏ lỡ một diễn viên ... xin lỗi –

Trả lời

7

Bạn chỉ có thể chuyển đổi một đại biểu theo cách này nếu các tham số và giá trị trả lại có thể được chuyển đổi bằng cách sử dụng biểu diễn bảo toàn chuyển đổi.

  • loại tham khảo chỉ có thể được chuyển đổi sang các loại tài liệu tham khảo khác theo cách này
  • giá trị Integral có thể được chuyển đổi sang giá trị khác số nguyên có cùng kích thước (int, uint, và đếm có cùng kích thước tương thích)

Một thêm vài bài viết trên blog có liên quan:

phân đôi này thúc đẩy được nêu ra sơ đồ phân loại khác cho các chuyển đổi (†). Chúng ta có thể chia các chuyển đổi thành các chuyển đổi bảo toàn đại diện (B thành D) và các chuyển đổi biểu diễn thay đổi (T thành U). (‡) Chúng ta có thể nghĩ về các chuyển đổi bảo toàn đại diện trên các loại tham chiếu như các chuyển đổi đó bảo toàn danh tính của đối tượng. Khi bạn bỏ một chữ B vào chữ D, bạn không làm gì với đối tượng hiện có; bạn chỉ đang xác minh rằng đó thực sự là loại bạn nói, và tiếp tục. Danh tính của đối tượng và các bit đại diện cho tham chiếu vẫn giữ nguyên. Nhưng khi bạn cast một int vào một double, các bit kết quả là rất khác nhau.

Đây là lý do tại sao chuyển đổi covariant và contravariant của giao diện và loại đại biểu yêu cầu tất cả các đối số kiểu khác nhau phải thuộc loại tham chiếu. Để đảm bảo rằng chuyển đổi tham chiếu biến thể luôn là bảo toàn danh tính, tất cả các chuyển đổi có liên quan đến các đối số kiểu cũng phải được giữ nguyên danh tính. Cách dễ nhất để đảm bảo rằng tất cả các chuyển đổi không tầm thường trên các đối số kiểu là bảo toàn nhận dạng là giới hạn chúng là chuyển đổi tham chiếu. http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx

"nhưng làm cách nào một loại giá trị, như int, 32 bit bộ nhớ, không hơn, không ít hơn, có thể kế thừa từ đối tượng? Một đối tượng được đặt trong bộ nhớ lớn hơn 32 bit; một khối đồng bộ hóa và một bảng chức năng ảo và tất cả các loại công cụ trong đó. " Dường như nhiều người nghĩ rằng sự thừa kế có liên quan đến cách một giá trị được đặt ra trong bộ nhớ. Nhưng làm thế nào một giá trị được đặt ra trong bộ nhớ là một chi tiết thực hiện, không phải là một nghĩa vụ hợp đồng của các mối quan hệ thừa kế! Khi chúng ta nói rằng int thừa hưởng từ đối tượng, điều chúng tôi muốn nói là nếu đối tượng có một thành viên - nói, ToString - thì int cũng có thành viên đó. http://ericlippert.com/2011/09/19/inheritance-and-representation/

4

Mặc dù tôi nghĩ @CodeInChaos là hoàn toàn đúng, tôi không thể giúp chỉ this Eric Lippert's blog bài ra.Trong thư trả lời để bình luận cuối cùng để đăng bài của mình (ở dưới cùng của trang) Eric giải thích lý do cho hành vi như vậy, và tôi nghĩ rằng đây là chính xác những gì bạn quan tâm.

+0

điều đó thật tuyệt - tôi gần như đã hỏi @CodeInChaos để tham khảo giải thích câu trả lời của anh ấy, nhưng thay vì cố gắng tìm một câu trả lời và không thể. Tôi nghĩ rằng (tôi nghĩ khi ý định của người gọi là rõ ràng, thì có thể lập luận rằng nó hợp lý cho thời gian chạy để thực hiện công việc nhiều hơn một chút) - nhưng cuối cùng cách nó hoạt động không có ý nghĩa. –

+0

@Andras: Đây chỉ là phỏng đoán của tôi, nhưng tôi nghĩ rằng hành vi thời gian chạy phải đồng nhất ngay từ đầu, trong khi tách những ý định rõ ràng khỏi những thứ tinh tế là một nhiệm vụ rất chủ quan. –

6

Dường như với tôi rằng. Net là không phù hợp trong nó điều trị hiệp phương sai - hoặc là một loại giá trị là một đối tượng hoặc nó không; nếu nó không phải là không nên phơi bày đối tượng như là một cơ sở

Nó phụ thuộc vào ý nghĩa của "là", như Tổng thống Clinton nổi tiếng nói.

Theo mục đích của hiệp phương sai , int là không phản đối vì int không phải là phân tương thích với đối tượng. Một biến kiểu đối tượng mong đợi một mẫu bit cụ thể với một ý nghĩa cụ thể được lưu trữ trong nó. Một biến kiểu int mong đợi một mẫu bit cụ thể với một ý nghĩa cụ thể, nhưng một khác nhau có nghĩa là ý nghĩa của một biến kiểu đối tượng.

Tuy nhiên, với mục đích thừa kế, một int là một đối tượngmỗi thành viên của đối tượng cũng là một thành viên của int. Nếu bạn muốn gọi một phương thức của đối tượng - ToString, nói trên int, bạn được đảm bảo rằng bạn có thể làm như vậy, bởi vì một int là một loại đối tượng, và một đối tượng có ToString. Đó là điều không may, tôi đồng ý, rằng giá trị chân lý của "một int là một đối tượng" thay đổi tùy thuộc vào việc bạn có nghĩa là "là tương thích gán với" hay "là một loại".

Nếu hiệp phương sai đó chỉ có thể đạt được thông qua thao tác quyền anh; thì khung công tác phải quan tâm đến điều đó.

OK. Ở đâu? Trường hợp vận hành quyền anh nên đi đâu? Ai đó, một nơi nào đó phải tạo ra một đoạn IL có hướng dẫn quyền anh. bạn đang gợi ý rằng khi khung nhìn:

Func<int> f1 =()=>1; 
Func<object> f2 = f1; 

sau đó khuôn khổ sẽ tự động giả vờ rằng bạn nói:

Func<object> f2 =()=>(object)f1(); 

và do đó tạo ra các hướng dẫn đấm bốc?

Đó là một tính năng hợp lý, nhưng hậu quả là gì? Func<int>Func<object> là các loại tham chiếu. Nếu bạn thực hiện f2 = f1 trên các loại tham chiếu như thế này, bạn có nghĩ rằng f2 và f1 có thông tin nhận dạng tham chiếu không? Nó sẽ không quá kỳ lạ đối với trường hợp thử nghiệm này không thành công?

f2 = f1; 
Debug.Assert(object.ReferenceEquals(f1, f2)); 

Bởi vì nếu khuôn khổ đã triển khai tính năng đó, nó sẽ.

Tương tự, nếu bạn nói:

f1 = MyMethod; 
f2 = f1; 

và bạn hỏi hai đại biểu cho dù họ được gọi cùng một phương pháp hay không, điều đó sẽ không được cực kỳ lạ nếu họ gọi phương pháp khác nhau?

Tôi nghĩ điều đó thật lạ. Tuy nhiên, các nhà thiết kế VB không. Nếu bạn cố gắng để kéo shenanigans như thế trong VB, trình biên dịch sẽ không dừng lại bạn. Trình tạo mã VB sẽ tạo các đại biểu không tham chiếu bằng nhau cho bạn tham chiếu đến các phương thức khác nhau. Hãy dùng thử!

Đạo đức của câu chuyện: có thể C# không phải là ngôn ngữ cho bạn. Có lẽ bạn thích một ngôn ngữ như VB, nơi ngôn ngữ được thiết kế để có một "đoán xem người dùng có thể có ý nghĩa gì và chỉ làm cho nó hoạt động" thái độ. Đó không phải là thái độ của các nhà thiết kế C#. Chúng tôi có nhiều hơn "nói với người dùng khi có điều gì đó có vẻ nghi ngờ sai và để họ tìm ra cách họ muốn sửa" loại người.

+1

Xin chào Eric - Tôi tiếp tục đặt câu hỏi đưa bạn vào không! Tôi chỉ muốn chỉ ra rằng câu hỏi của tôi là chống lại. Net ở đây, không C# - có nguy cơ nghe giống như một mã nazi; bạn không thể trả tiền cho tôi để quay lại VB; C++ là ngôn ngữ yêu thích của tôi * cho đến khi C# xuất hiện :) Có những ngữ nghĩa không may ở đây, nhưng quan điểm của bạn về bản dịch giữa một loại đại biểu và loại khác là thanh lịch và đủ để tôi hài lòng rằng nó được thực hiện đúng cách. Dù sao, tôi thích xây dựng cây biểu hiện, vì vậy tôi vẫn có thể được biện minh khi làm như vậy. Cảm ơn câu trả lời của bạn, rất rõ ràng :) –

+0

Khi bạn nói về những gì các nhà thiết kế VB.NET cho phép, bạn có cho rằng mọi người sử dụng phương ngữ 'Tùy chọn nghiêm ngặt tắt '? Hầu hết những điều bạn phàn nàn với VB chỉ áp dụng cho phương ngữ 'Option Strict Off', cho đến nay tôi có thể nói là hầu như bị khinh thường và không bao giờ được sử dụng bởi bất kỳ lập trình viên VB.NET nghiêm túc nào ngoại trừ trường hợp cực kỳ hẹp bắt buộc. – supercat

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