2010-06-13 15 views
40

Tôi đang tìm cách sửa đổi thuộc tính trên đối tượng C# 4.0 dynamic với tên thuộc tính chỉ được biết khi chạy.Cách đặt thuộc tính của đối tượng động C# 4 khi bạn có tên trong biến khác

Có cách nào để làm điều gì đó tương tự (ExpandoObject chỉ được sử dụng như một ví dụ, đây có thể là bất kỳ lớp mà thực hiện IDynamicMetaObjectProvider):

string key = "TestKey"; 
dynamic e = new ExpandoObject(); 
e[key] = "value"; 

Đó sẽ là tương đương với:

dynamic e = new ExpandoObject(); 
e.TestKey = "value"; 

Hoặc là cách duy nhất để chuyển tiếp phản ánh?

+0

Phản ánh có lẽ là giải pháp của riêng bạn, trừ khi bạn đặt các thuộc tính của mình thành một bản đồ băm để chúng có thể được xác định khi chạy. –

+0

có thể trùng lặp của các thuộc tính [Thêm không xác định (tại thời điểm thiết kế) vào một ExpandoObject] (http://stackoverflow.com/questions/2974008/adding-unknown-at-design-time-properties-to-an-expandoobject) – nawfal

Trả lời

16

Không phải rất dễ dàng, không. Phản ánh không hoạt động vì nó giả định kiểu mô hình thông thường, là không phải là phạm vi đầy đủ của dynamic. Nếu bạn đang thực sự chỉ nói chuyện với các đối tượng thông thường, thì chỉ cần sử dụng sự phản chiếu ở đây. Nếu không, tôi hy vọng bạn có thể muốn đảo ngược kỹ sư mã mà trình biên dịch phát ra cho một nhiệm vụ cơ bản và tinh chỉnh nó để có một tên thành viên linh hoạt. Tôi sẽ thành thật, mặc dù: đây không phải là một lựa chọn hấp dẫn; một đơn giản:

dynamic foo = ... 
foo.Bar = "abc"; 

dịch để:

if (<Main>o__SiteContainer0.<>p__Site1 == null) 
{ 
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) })); 
} 
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc"); 

Nếu bạn muốn một phương pháp mà làm việc cho cả hai đối tượng năng động và không động: FastMember rất thuận tiện cho việc này, và làm việc tại một trong hai loại hoặc cấp đối tượng:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]); 

có sẵn trên Nuget và được tối ưu hóa nhiều cho cả động và không động enarios.

+0

Cảm ơn Marc - Tôi nghĩ rằng tôi đã bỏ lỡ một cái gì đó hiển nhiên nhưng rõ ràng là không! :) Bạn đã có bất kỳ liên kết đến một ví dụ tốt về làm thế nào để làm phát xạ mã để tôi có thể cung cấp cho điều này một đi? Nếu tôi có thể làm cho nó hoạt động như thế này –

+0

@Kieran - nó phụ thuộc vào những gì bạn cần. Nó không * âm thanh * giống như bạn cần phát xạ (mã tôi đăng tải chỉ là mẫu của nó xấu đến mức nào). –

+0

Bị bỏ qua vì điều này không giải thích rằng expandoObject sử dụng từ điển làm cửa hàng sao lưu và có thể được sử dụng chính xác như OP ban đầu được đề xuất. Không cần phản xạ HOẶC phát ra trên expandoObject. Điều đó nói rằng @MarcGravell là chính xác cho các đối tượng động không mở rộng (cũng như các đối tượng thông thường được gán cho một var động) không có một giải pháp sạch. –

3

Khung nguồn mở của tôi Dynamitey có các phương thức để gọi dựa trên tên chuỗi bằng cách sử dụng DLR. Nó làm công việc của bộ nhớ đệm các trang web ràng buộc và sắp xếp hợp lý nó xuống một cuộc gọi phương thức. nó cũng chạy nhanh hơn so với sự phản chiếu trên các đối tượng không động.

Dynamic.InvokeSet(e, "TestKey", "value"); 
50

Paul Sasik đã trả lời một câu hỏi tương tự tại C# 4.0 Dynamic vs Expando... where do they fit?

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     var p = expando as IDictionary<String, object>; 
     p["A"] = "New val 1"; 
     p["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
0

fast-member có thể phù hợp với những hóa đơn - có vẻ như nó sẽ sinh ra IL khi đang bay, nhưng lưu trữ nó để nó thực sự nhanh sau khi sử dụng đầu tiên .

6

Để thêm vào câu trả lời của Jonas, bạn không phải tạo một var p mới. Thay vào đó, hãy sử dụng phương pháp này:

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     ((IDictionary<String, object>)expando)["A"] = "New val 1"; 
     ((IDictionary<String, object>)expando)["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
Các vấn đề liên quan