2013-07-24 32 views
5

Giả sử tôi đã xác định Func như sau:Nhận "Object" kiểu trả về từ Func <MyClass, đối tượng>

Func<MyClass, object> f = o => o.StringProperty; 

hoặc

Func<MyClass, object> f = o => o.Property.SomeMethod(); 

Có cách để có được những thực tế trở lại loại mà không cần gọi nó là gì?

+0

"Nhận" loại trả về chính xác như thế nào? Một ví dụ giả định? – Jon

+0

@ Jon Tôi nghĩ rằng suy ra kiểu trả về dựa trên thực tế trình biên dịch biết điều này ('string' trong ví dụ đầu tiên, trả về' SomeMethod' trong lần thứ hai), tuy nhiên ví dụ này sẽ sụp đổ tại thời gian chạy dưới dạng biến 'f 'có thể được gán lại. Giả sử, nếu 'f' không thể được gán lại, bạn có thể xác định tĩnh kiểu trả về bằng cách phân tích định nghĩa' Func'. Âm thanh như một ứng cử viên cho Roslyn ;-) –

+0

Tùy thuộc vào mức sử dụng của bạn, có lẽ bạn có thể tận dụng API 'Expression' để kiểm tra kiểu trả về: http://stackoverflow.com/questions/671968/retrieving-property-name-from- lambda-expression (đây không phải là việc thực hiện chính xác những gì bạn đang cố gắng làm, chỉ là một ví dụ liên quan) –

Trả lời

4

Bạn có thể lấy kiểu trả về như thế này:

Nhưng điều này sẽ đưa bạn trở loại object. Nếu bạn muốn nhận được Method hoặc String hoặc cái gì đó có nguồn gốc từ object, bạn sẽ không thể có thông tin trừ khi bạn gọi phương thức.

Trên thực tế bạn có thể, nhưng điều này có nghĩa là bạn sẽ phải Thao thiêt lõi phương pháp và sau đó phân tích nó để xem những gì phương pháp này có thể trở lại. Nhưng thậm chí sau đó, có thể có nhiều loại trả lại khác nhau. Vì vậy, câu trả lời là: nếu bạn muốn biết rằng nó trả về một đối tượng, sau đó có bạn có thể, nếu không nó không có giá trị rắc rối, và nó là tốt hơn để tìm một cách khác để làm những gì bạn cần. Các tính năng chính của nó là tốt nhất.

1

Bạn có thể làm một cái gì đó gần với điều này bằng cách sử dụng một phương pháp tổng quát để làm cho trình biên dịch suy ra những lập luận kiểu:

static Func<T1, R> Infer<T1, R>(Func<T1, R> f) { return f; } 

Và sau đó:

var func = Infer((string s) => s.Length); 

này sẽ mã hóa các kiểu trả về vào loại func lúc biên dịch.

Tất nhiên để có giải pháp áp dụng chung hơn, bạn sẽ cần một loạt quá tải Infer để trang trải ActionFunc với một, hai, ba, v.v.

Nếu sau đó bạn muốn nhận loại trả về khi chạy, đối với bất kỳ loại nào Func thì đơn giản như func.Method.ReturnType như ppetrov đã chỉ ra.

+0

nó hoạt động cho đến khi chúng tôi đã tạo ra thể hiện của Func. –

+0

@ EugeneD.Gubenkov: Tất nhiên, nếu bạn đã có 'Func f = s => s.Length' và bạn làm' Infer (f) 'bạn sẽ lấy lại một' Func '- không có cách nào để khôi phục thông tin loại đã bị" mất ". – Jon

+1

@ EugeneD.Gubenkov: Nếu bạn muốn làm nhiều hơn thì bạn phải làm việc với 'Expression >' và phản chiếu thay thế. – Jon

2

Vì bạn đang lấy những Func<MyClass, object> đại biểu trong thời gian chạy từ các nguồn khác, các loại thông tin được về cơ bản bị mất. Thay vào đó, khi các chức năng này được xác định, bạn có thể có những người gọi về cơ bản mã hóa thông tin kiểu đó trong một đại biểu được bọc bằng cách tận dụng LINQ Expression APIAPI (EDIT: Silly me, đơn giản hơn nhiều tại thời điểm này) có thông tin thời gian biên dịch generic):

public class MyClassDelegate 
{ 
    private readonly Func<MyClass, object> Function; 

    public Type ReturnType { get; private set; } 

    private MyClassDelegate(Func<MyClass, object> function, Type returnType) 
    { 
     this.Function = function; 
     this.ReturnType = returnType; 
    } 

    public object Invoke(MyClass context) 
    { 
     return Function(context); 
    } 

    public static MyClassDelegate Create<TReturnType>(Func<MyClass, TReturnType> function) 
    { 
     Func<MyClass, object> nonTypedFunction = o => function(o); 
     return new MyClassDelegate(nonTypedFunction, typeof(TReturnType)); 
    } 
} 

(Một chung lớp có nguồn gốc MyClassDelegate<TReturnType> : MyClassDelegate thể được thực hiện cũng như để có được xung quanh một số các sillyness trong phương pháp Create, hoặc tránh giá trị kiểu đấm bốc, hoặc có sự trở lại loại thông tin có sẵn tại thời gian biên dịch hoặc thậm chí bằng cách phản ánh trên bất cứ điều gì MyClassDelegate<TReturnType> là.)

Người gọi xác định các đại biểu thay vì làm việc trực tiếp với một Func<MyClass, object> thay vào đó sẽ làm việc với các lớp này và xác định các đại biểu của họ như:

MyClassDelegate f1 = MyClassDelegate.Create(o => o.StringProperty); 
MyClassDelegate f2 = MyClassDelegate.Create(o => o.Property.SomeMethod()); 

API của bạn sẽ yêu cầu một MyClassDelegate, mà bạn có thể dễ dàng truy cập vào các loại của họ :

Console.WriteLine(f1.ReturnType.FullName); //string 
Console.WriteLine(f2.ReturnType.FullName); //whatever `SomeMethod()` is declared to return 

Cuối cùng, bạn có thể gọi các đại biểu hoặc thậm chí tạo Func<MyClass, object> đại biểu vẫn còn:

f1.Invoke(myClassInstance); 
Func<MyClass, object> f3 = f1.Invoke; 
+0

@ EugeneD.Gubenkov: Tôi đã chỉnh sửa câu trả lời của mình; API biểu thức LINQ là không cần thiết vào thời điểm này. Chìa khóa takeaway là để có người gọi mã hóa thông tin này khi đại biểu được tạo ra và tận dụng một đại biểu bọc để vượt qua các thông tin trong suốt API của bạn. –

+0

Cảm ơn bạn đã trả lời chi tiết. Khi chúng ta muốn làm những việc như vậy, chúng ta nên sử dụng Expressions, tôi hiểu rồi. –

+0

Cách sử dụng 'Expression' thực sự chỉ giúp nếu bạn muốn tránh suy luận chung. Tức là, bạn có thể có 'Tạo (biểu thức > chức năng)' và vẫn xác định loại được sử dụng. Nhưng bạn cũng có thể tận dụng lợi thế của suy luận kiểu chung tôi đã sử dụng trong bản cập nhật của mình. –

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