2012-02-29 28 views

Trả lời

39

Trong quá khứ với .NET, đã có sự căng thẳng giữa mong muốn có thể tự động hóa một số tính năng nhất định thông qua phản ánh và có thể tùy chỉnh chúng. Ví dụ, lấy bảng Properties trong Visual Studio - trong các trường hợp hiển thị một số loại .NET (ví dụ, một điều khiển trên bề mặt thiết kế), nó có thể tự động khám phá và hiển thị mọi thuộc tính công khai mà kiểu xác định.

Sử dụng phản ánh cho loại hành vi loại hướng này là hữu ích vì điều đó có nghĩa là mọi thuộc tính sẽ hiển thị mà không có nhà phát triển kiểm soát cần làm bất cứ điều gì. Nhưng nó trình bày một vấn đề: nếu bạn muốn tùy chỉnh mọi thứ, ví dụ: xác định phân loại hoặc giao diện người dùng chỉnh sửa tùy chỉnh cho một thuộc tính cụ thể?

Giải pháp cổ điển trong .NET là tát một loạt thuộc tính tùy chỉnh vào các thành viên có liên quan. Tuy nhiên, một vấn đề với điều đó có nghĩa là các phần của mã của bạn thực hiện một công việc có ý nghĩa trong thời gian chạy tùy thuộc vào các lớp chỉ làm bất cứ điều gì vào thời gian thiết kế - dựa vào các thuộc tính ngăn bạn tách ra các thời gian chạy và thiết kế . Bạn có thực sự muốn gửi mã cho giao diện người dùng thiết kế tùy chỉnh cho bảng thuộc tính của VS như là một phần của thư viện điều khiển sẽ kết thúc trên các máy người dùng cuối không?

Một vấn đề khác là trong một số trường hợp, bạn có thể muốn quyết định động 'những thuộc tính' bạn hiện diện. Một trong những ví dụ lâu đời nhất về điều này (có niên đại từ .NET 1.0) đã đặt một DataSet trong một số loại điều khiển lưới (cả phía máy khách hoặc web). Với tập dữ liệu được đánh máy mạnh, phản ánh có thể là cách thích hợp để lưới khám phá các thuộc tính mà nguồn cung cấp, nhưng DataSet cũng có thể được sử dụng động, vì vậy bạn cần một cách để lưới dữ liệu yêu cầu thời gian chạy các cột hiển thị.

(Một câu trả lời cho điều này là: thiết kế giao diện người dùng của bạn đúng cách! Tạo lưới trực tiếp như thế này dẫn đến trải nghiệm người dùng khủng khiếp. Tuy nhiên, rất nhiều người muốn làm điều đó một cách lười biếng, cho dù đó là một ý hay hay không .. .)

Vì vậy, bạn có một tình huống đôi khi bạn muốn hành vi phản chiếu, nhưng đôi khi bạn muốn kiểm soát toàn bộ thời gian chạy.

Các giải pháp quảng cáo khác nhau nổi lên cho điều này. Bạn có toàn bộ các loại hình TypeDescriptorPropertyDescriptor, cung cấp một loại chế độ xem ảo hóa ở phía trên cùng của sự phản chiếu. Theo mặc định, điều này sẽ vượt qua mọi thứ trực tiếp từ phản xạ nhưng các loại có cơ hội chọn tham gia cung cấp các mô tả tùy chỉnh trong thời gian chạy, cho phép chúng sửa đổi hoặc thậm chí thay thế hoàn toàn cách chúng trông như thế nào. ICustomTypeDescriptor là một phần của thế giới đó.

Điều đó cung cấp một giải pháp cho vấn đề mong muốn hành vi theo hướng phản chiếu theo mặc định với tùy chọn cung cấp hành vi theo thời gian chạy nếu bạn muốn. Nhưng nó không giải quyết được vấn đề mà bạn chỉ muốn làm điều này trong thời gian thiết kế, và bạn không muốn phải gửi mã đó như là một phần của redistributables thời gian chạy của bạn.

Vì vậy, một vài năm trước đây, Visual Studio đã giới thiệu các cơ chế đặc biệt của riêng mình để tăng thông tin loại tại thời điểm thiết kế.Có một loạt các hành vi theo quy ước, trong đó Visual Studio sẽ tự động khám phá các thành phần thiết kế thời gian liên quan đến các thành phần thời gian chạy cụ thể, cho phép bạn tùy chỉnh trải nghiệm thiết kế mà không cần phải đưa mã liên quan vào các phân phối lại của bạn. Blend cũng sử dụng cơ chế này, mặc dù với một số chỉnh sửa, làm cho nó có thể cung cấp các phần thiết kế khác nhau cho VS và Blend.

Tất nhiên, không ai trong số đó có thể nhìn thấy thông qua các API phản ánh bình thường - VS và Blend có một lớp bao bọc nằm trên đầu trang của sự phản ánh để làm cho tất cả điều này hoạt động.

Vì vậy, bây giờ chúng tôi đã có hai lớp ảo hóa có thể đi qua để phản chiếu, hoặc có thể làm tăng thêm những gì đi ra khỏi suy nghĩ ...

Dường như trong .NET 4.5, đội CLR quyết định rằng kể từ khác nhau các nhóm đã làm điều này, và các nhóm khác muốn làm nhiều hơn nữa (nhóm MEF có các yêu cầu tương tự cho hành vi phản xạ-với-tùy chọn-thời gian chạy-tăng cường), đây chính xác là thứ cần được xây dựng trong thời gian chạy.

Mô hình mới có vẻ như thế này: lớp cơ sở ReflectionContext là một API trừu tượng mà qua đó bạn có thể nhận được phiên bản ảo của API phản chiếu. Nó đơn giản, bởi vì một trong những ý tưởng chính là bạn không còn cần các API chuyên dụng như hệ thống mô tả kiểu nếu mục tiêu duy nhất của bạn là có được một trình bao bọc ảo trên đỉnh của sự phản chiếu - sự phản chiếu bây giờ có thể ảo hóa ra khỏi hộp. Vì vậy, bạn có thể viết các loại điều này

public static void ShowAllAttributes(Type t) 
{ 
    foreach (Attribute attr in t.GetCustomAttributes(true)) 
    { 
     Console.WriteLine(attr); 
    } 
} 

Bây giờ bạn đã luôn luôn có thể viết đó, nhưng trước khi .NET 4.5, mã như thế này sẽ luôn luôn được làm việc chống lại loại thông tin 'thực tế' vì nó sử dụng Reflection . Nhưng nhờ vào các bối cảnh phản chiếu, bây giờ có thể cung cấp điều này với một virtualized Type. Vì vậy, xem xét loại rất nhàm chán này:

class NoRealAttributes 
{ 
} 

Nếu bạn chỉ cần vượt qua typeof(NoRealAttributes) phương pháp ShowAllAttributes của tôi, nó sẽ in ra gì cả. Nhưng tôi có thể viết một (hơi giả tạo) phản ánh phong tục ngữ cảnh:

class MyReflectionContext : CustomReflectionContext 
{ 
    protected override IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes) 
    { 
     if (member == typeof(NoRealAttributes)) 
     { 
      return new[] { new DefaultMemberAttribute("Foo") }; 
     } 
     else 
     { 
      return base.GetCustomAttributes(member, declaredAttributes); 
     } 
    } 
} 

(Bằng cách này, tôi nghĩ rằng sự khác biệt giữa CustomReflectionContext và cơ sở của nó, ReflectionContext là sau này định nghĩa các API cho một bối cảnh suy virtualizable, trong khi CustomReflectionContext thêm một số người giúp đỡ để làm cho nó dễ dàng hơn cho bạn để thực hiện một điều như vậy) Và bây giờ tôi có thể sử dụng để cung cấp một phiên bản ảo của Type cho lớp học của tôi:.

var ctx = new MyReflectionContext(); 
Type mapped = ctx.MapType(typeof(NoRealAttributes).GetTypeInfo()); 
ShowAllAttributes(mapped); 

trong mã này, mapped vẫn đề cập đến đối tượng Type , vì vậy bất cứ điều gì mà biết cách sử dụng API phản chiếu sẽ có thể làm việc với nó, nhưng bây giờ nó sẽ báo cáo sự hiện diện của một thuộc tính không thực sự ở đó. Tất nhiên, Type là trừu tượng, vì vậy chúng tôi luôn có thứ gì đó có nguồn gốc từ đó và nếu bạn gọi mapped.GetType() bạn sẽ thấy rằng đó thực sự là một số System.Reflection.Context.Custom.CustomType thay vì số System.RuntimeType bạn thường thấy. Và đối tượng CustomType đó thuộc về ngữ cảnh tùy chỉnh của tôi, vì vậy bất kỳ đối tượng API phản chiếu nào khác mà bạn nắm giữ thông qua nó (ví dụ: nếu bạn viết mapped.Assembly.GetTypes()), bạn cũng sẽ nhận được các đối tượng tùy chỉnh trải qua ngữ cảnh tùy chỉnh của mình. sửa đổi bất cứ điều gì khác mà đi ra.

Vì vậy, mã có thể điều hướng thông qua hệ thống kiểu bằng cách sử dụng đối tượng Type được tùy chỉnh.Mặc dù mã như vậy đang sử dụng API phản chiếu cơ bản thông thường, giờ đây tôi có cơ hội tùy chỉnh bất kỳ thứ gì xuất phát từ đó nếu tôi thấy phù hợp.

Bạn chỉ nhận được chế độ xem ảo hóa này nếu bạn yêu cầu. Ví dụ, MEF trong .NET 4.5 tìm kiếm một thuộc tính tùy chỉnh xác định rằng nó nên sử dụng một bối cảnh phản chiếu tùy chỉnh do người dùng cung cấp, nhưng sẽ quay trở lại với sự phản chiếu thông thường nếu không. (Và trong trường hợp của phương pháp ShowAllAttributes của tôi, nó sử dụng bất kỳ đối tượng Type nào tôi chọn để chuyển vào - nó không biết nếu nó nhận được một đối tượng ảo hóa hoặc một loại thực ''.)

Vì vậy, trong ngắn hạn, điều này có nghĩa là bạn không còn cần trình bao bọc ad hoc xung quanh API phản chiếu nếu bạn muốn thông tin kiểu ảo hóa.

+0

Đây là câu trả lời hay, cảm ơn bạn rất nhiều. Tôi thực sự hy vọng họ sẽ cho phép ảo hóa bất kỳ cuộc gọi phản chiếu hiện có nào (có thể đã giúp với rất nhiều hạn chế dựa trên sự phản chiếu không cần thiết trong các thư viện MS và không phải MS hiện có). Vì vậy, đây chỉ là một trình trợ giúp được tích hợp sẵn cho những gì chúng tôi có thể làm. Vâng, điều đó cũng có thể hữu ích. –

+0

Tôi có thể giải thưởng bao nhiêu điểm? Đối với một điều, bạn đã khiến tôi nhận thấy rằng điều tối nghĩa này tôi đã bỏ qua (thông qua phản xạ lưới) là thực sự mới với 4.5 và không phải là một trong những clunk bị bỏ rơi interop. NET có rất nhiều (bạn đề cập đến một vài). Bonanza! Nếu tôi có thể ảo hóa một thuộc tính trên một loại on-the-fly (như đã hứa trong tài liệu) và có chúng hiển thị trong VS intellisense, tôi sẽ khóc với niềm vui. Nevermind tất cả thời gian tôi lãng phí trên 1.001 nỗ lực thất bại (mới nhất và có lẽ tồi tệ nhất đã cố gắng để hoàn toàn phân lớp chòm sao 'System.Type'). Woo-hoo đi thử cái này ... –

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