2009-12-15 41 views
36

tôi có mã:Cast đại biểu đến Func trong C#

public delegate int SomeDelegate(int p); 

public static int Inc(int p) { 
    return p + 1; 
} 

tôi có thể đúc Inc-SomeDelegate hoặc Func<int, int>:

SomeDelegate a = Inc; 
Func<int, int> b = Inc; 

nhưng tôi không thể cast Inc-SomeDelegate và sau đó dàn diễn viên đến Func<int, int> theo cách thông thường như sau:

Func<int, int> c = (Func<int, int>)a; // Сompilation error 

Tôi có thể làm như thế nào?

Trả lời

43
SomeDelegate a = Inc; 
Func<int, int> b = Inc; 

là viết tắt của

SomeDelegate a = new SomeDelegate(Inc); // no cast here 
Func<int, int> b = new Func<int, int>(Inc); 

Bạn không thể cast một thể hiện của SomeDelegate đến một Func < int, int > vì lý do tương tự bạn không thể truyền một chuỗi tới một từ điển < int, int > - chúng là các loại khác nhau.

này hoạt động:

Func<int, int> c = x => a(x); 

đó là cú pháp đường cho

class MyLambda 
{ 
    SomeDelegate a; 
    public MyLambda(SomeDelegate a) { this.a = a; } 
    public int Invoke(int x) { return this.a(x); } 
} 

Func<int, int> c = new Func<int, int>(new MyLambda(a).Invoke); 
+0

Có thể nhấn mạnh vào thực tế các công cụ tùy chỉnh bạn đã viết thường được thực hiện bằng phép thuật biên dịch. – Dykam

+0

+1 để được giải thích tốt đẹp. Có một cách đơn giản hơn * Func c = x => a (x); * mặc dù - xem câu trả lời của tôi. –

+1

Tôi giả sử trong bit cuối cùng 'công khai Foo' nên đã được 'public MyLambda'? – Chris

23

Hãy thử điều này:

Func<int, int> c = (Func<int, int>)Delegate.CreateDelegate(typeof(Func<int, int>), 
                  b.Target, 
                  b.Method); 
+0

Hãy xem câu trả lời của tôi để có cách dễ dàng hơn để đạt được điều này. –

+0

Xấu xí nhưng hữu ích nếu bạn không thể đủ khả năng để nói rằng 'Func c = b.Invoke; '. Nhưng có một điều cần nhớ: Trong .NET, tất cả các loại đại biểu là các đại biểu "multicast", và mã trên sẽ chỉ dùng phương thức ** ** cuối cùng trong danh sách yêu cầu. Nếu bạn không thể đảm bảo danh sách yêu cầu chỉ có một thành viên, bạn sẽ phải lặp qua nó, tôi đoán vậy. –

+0

@JeppeStigNielsen Tôi không nghĩ rằng một multicast 'Func' có ý nghĩa nhiều. Bạn sẽ sử dụng giá trị trả lại nào? –

7

Vấn đề là:

SomeDelegate a = Inc; 

Không phải là thực sự là một diễn viên. Đó là dạng ngắn:

SomeDelegate a = new SomeDelegate(Inc); 

Do đó không có diễn viên. Một giải pháp đơn giản cho vấn đề của bạn có thể này (trong 3,0 C#)

Func<int,int> f = i=>a(i); 
+1

Vấn đề với điều đó là bạn đang thực sự gói các đại biểu với một cái mới sử dụng một phương pháp vô danh, trong đó có một chi phí. –

+2

Vâng, bạn nói đúng. Mã thanh lịch so với hiệu suất. Phụ thuộc vào bạn cần những gì bạn chọn. – Gamlor

4

Đó là cùng một loại vấn đề như thế này:

public delegate int SomeDelegate1(int p); 
public delegate int SomeDelegate2(int p); 
... 
    SomeDelegate1 a = new SomeDelegate1(Inc); 
    SomeDelegate2 b = (SomeDelegate2)a; // CS0030 

đó là cùng một loại vấn đề như:

public class A { int prop { get; set; } } 
public class B { int prop { get; set; } } 
... 
    A obja = new A(); 
    B objb = (B)obja; // CS0029 

Đối tượng không thể được đúc từ loại này sang loại khác không liên quan, mặc dù các loại khác hoàn toàn tương thích. Vì thiếu một thuật ngữ tốt hơn: một đối tượng có định danh kiểu mà nó mang theo khi chạy. Không thể thay đổi danh tính đó sau khi đối tượng được tạo. Biểu thức hiển thị của danh tính này là Object.GetType().

52

Có một cách đơn giản hơn nhiều để làm điều đó, mà tất cả các câu trả lời khác đã bỏ lỡ:

Func<int, int> c = a.Invoke; 

Xem this blog post để biết thêm.

+3

Đẹp. Nó tương tự như giải pháp của Gamlor, nhưng không có phương pháp nặc danh. Tuy nhiên, nó bao gồm các đại biểu ban đầu, không giống như giải pháp đề xuất của tôi. –

+7

Nếu bất kỳ ai không chắc chắn về ý nghĩa của Diego, hãy xem các thuộc tính Mục tiêu và Phương thức của đại biểu 'a' ban đầu và đại biểu 'c'. Với cơ chế của Diego, 'c' trỏ trực tiếp vào phương thức ban đầu giống như 'a'. Với phương pháp của Winston, nó không - nó chỉ cho đại biểu mà lần lượt trỏ đến phương pháp ban đầu, vì vậy bạn nhận được một mức độ không cần thiết của indirection. –

+2

Như Diego và Ian đã đề cập, giải pháp này kết thúc tốt đẹp bản gốc của đại biểu ban đầu. Do đó, nếu bạn đang sử dụng giải pháp này với các sự kiện, bạn sẽ không thể hủy đăng ký. Với giải pháp của Diego bạn có thể. – Verax

7

Tác phẩm này (trong C# 4.0 ít nhất - không được thử trong các phiên bản cũ hơn):

SomeDelegate a = Inc; 
Func<int, int> c = new Func<int, int>(a); 

Nếu bạn nhìn vào IL, điều này được biên dịch chính xác giống như câu trả lời của Winston. Đây là IL cho dòng thứ hai của những gì tôi chỉ viết:

ldloc.0 
ldftn  instance int32 ConsoleApplication1.Program/SomeDelegate::Invoke(int32) 
newobj  instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object, native int) 

Và đó cũng là một cách chính xác những gì bạn thấy nếu bạn gán a.Invoke vào c.

Ngẫu nhiên, mặc dù giải pháp của Diego hiệu quả hơn, trong đó đại biểu kết quả tham chiếu trực tiếp đến phương thức cơ bản hơn là đi qua đại biểu khác, nó không xử lý chính xác các đại biểu multicast. Giải pháp của Winston, bởi vì nó chỉ hoàn toàn chống lại các đại biểu khác. Nếu bạn muốn có một giải pháp trực tiếp mà cũng xử lý các đại biểu với nhiều mục tiêu, bạn cần một chút gì đó phức tạp hơn:

public static TResult DuplicateDelegateAs<TResult>(MulticastDelegate source) 
{ 
    Delegate result = null; 
    foreach (Delegate sourceItem in source.GetInvocationList()) 
    { 
     var copy = Delegate.CreateDelegate(
      typeof(TResult), sourceItem.Target, sourceItem.Method); 
     result = Delegate.Combine(result, copy); 
    } 

    return (TResult) (object) result; 
} 

này là điều đúng đắn cho các đại biểu với một mục tiêu duy nhất bằng cách-nó sẽ kết thúc sản xuất chỉ một đại biểu duy nhất của loại mục tiêu đề cập trực tiếp đến bất kỳ phương thức nào (và khi có thể, đối tượng) mà đại biểu đầu vào được nhắc tới.

+0

Tôi tự hỏi tại sao 'Phương thức' và' Mục tiêu' của một điểm đại biểu đa cuộc gọi đến một trong các mục tiêu của nó. Tôi nghĩ sẽ có ý nghĩa hơn khi có 'Target' đích của chính mình, và có' Method' trỏ tới phương thức 'Invoke' của nó [nó sẽ kiểm tra xem nó đã tự gọi nó hay không, và nếu như vậy, sử dụng multicast danh sách] hoặc phương thức "gọi multicast". Điều đó sẽ tránh được nguy cơ vô tình biến một đại biểu đa cuộc gọi thành một cuộc gọi đơn lẻ. – supercat

+0

Toàn bộ tình huống xung quanh 'MulticastDelegate' là một mớ hỗn độn, bởi vì Microsoft đã thay đổi suy nghĩ của họ về cách xử lý điều này khá muộn trong ngày. Trong bản xem trước công khai đầu tiên của .NET, một số đại biểu đã phát đa hướng và một số thì không. Họ cuối cùng đã quyết định từ bỏ sự khác biệt này nhưng không thực sự có thời gian để dọn dẹp mọi thứ, điều này khiến cho một số bất thường trong hệ thống phân cấp loại đại biểu. –

4

Bạn có thể hack một diễn viên bằng cách sử dụng một thủ thuật mà bạn sử dụng C# tương đương của một công đoàn C++. Phần khó khăn là cấu trúc với hai thành viên có [FieldOffset (0)]:

[TestFixture] 
public class Demo 
{ 
    public void print(int i) 
    { 
     Console.WriteLine("Int: "+i); 
    } 

    private delegate void mydelegate(int i); 

    [StructLayout(LayoutKind.Explicit)] 
    struct funky 
    { 
     [FieldOffset(0)] 
     public mydelegate a; 
     [FieldOffset(0)] 
     public System.Action<int> b; 
    } 

    [Test] 
    public void delegatetest() 
    { 
     System.Action<int> f = print; 
     funky myfunky; 
     myfunky.a = null; 
     myfunky.b = f; 

     mydelegate a = myfunky.a; 

     a(5); 
    } 
} 
+3

Lần đầu tiên tôi nghe nói về cái hack tuyệt vời nguy hiểm này. Cảm ơn! – Ashe

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