Những lợi ích thực sự của ExpandoObject là gì?


587

Lớp ExpandoObject được thêm vào .NET 4 cho phép bạn tùy ý đặt các thuộc tính vào một đối tượng khi chạy.

Có bất kỳ lợi thế nào cho việc này hơn là sử dụng một Dictionary<string, object>, hoặc thậm chí là một Hashtable không? Theo như tôi có thể nói, đây không là gì ngoài một bảng băm mà bạn có thể truy cập với cú pháp ngắn gọn hơn một chút.

Ví dụ, tại sao lại thế này:

dynamic obj = new ExpandoObject();
obj.MyInt = 3;
obj.MyString = "Foo";
Console.WriteLine(obj.MyString);

Thực sự tốt hơn, hoặc khác biệt đáng kể, hơn:

var obj = new Dictionary<string, object>();
obj["MyInt"] = 3;
obj["MyString"] = "Foo";

Console.WriteLine(obj["MyString"]);

Những lợi thế thực sự nào đạt được bằng cách sử dụng ExpandoObject thay vì chỉ sử dụng một loại từ điển tùy ý, khác với việc bạn không sử dụng một loại sẽ được xác định khi chạy.

Câu trả lời:


689

Vì tôi đã viết bài báo MSDN mà bạn đang đề cập, tôi đoán tôi phải trả lời bài này.

Đầu tiên, tôi dự đoán câu hỏi này và đó là lý do tại sao tôi đã viết một bài đăng trên blog cho thấy trường hợp sử dụng thực tế ít nhiều cho ExpandoObject: Dynamic in C # 4.0: Giới thiệu ExpandoObject .

Một thời gian ngắn, ExpandoObject có thể giúp bạn tạo các đối tượng phân cấp phức tạp. Ví dụ: hãy tưởng tượng rằng bạn có một từ điển trong từ điển:

Dictionary<String, object> dict = new Dictionary<string, object>();
Dictionary<String, object> address = new Dictionary<string,object>();
dict["Address"] = address;
address["State"] = "WA";
Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]);

Sâu hơn là thứ bậc, xấu hơn là mã. Với ExpandoObject, nó vẫn thanh lịch và dễ đọc.

dynamic expando = new ExpandoObject();
expando.Address = new ExpandoObject();
expando.Address.State = "WA";
Console.WriteLine(expando.Address.State);

Thứ hai, như đã được chỉ ra, ExpandoObject triển khai giao diện INotifyPropertyChanged cho phép bạn kiểm soát nhiều thuộc tính hơn từ điển.

Cuối cùng, bạn có thể thêm các sự kiện vào ExpandoObject như ở đây:

class Program
{
   static void Main(string[] args)
   {
       dynamic d = new ExpandoObject();

       // Initialize the event to null (meaning no handlers)
       d.MyEvent = null;

       // Add some handlers
       d.MyEvent += new EventHandler(OnMyEvent);
       d.MyEvent += new EventHandler(OnMyEvent2);

       // Fire the event
       EventHandler e = d.MyEvent;

       e?.Invoke(d, new EventArgs());
   }

   static void OnMyEvent(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent fired by: {0}", sender);
   }

   static void OnMyEvent2(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent2 fired by: {0}", sender);
   }
}

Ngoài ra, hãy nhớ rằng không có gì ngăn cản bạn chấp nhận các đối số sự kiện theo cách năng động. Nói cách khác, thay vì sử dụng EventHandler, bạn có thể sử dụng EventHandler<dynamic>, điều này sẽ gây ra đối số thứ hai của trình xử lý dynamic.


53
Hấp dẫn. Cảm ơn thông tin lại: sự kiện. Đó là một cái mới cho tôi.
Sậy Copsey

16
@AlexandraRusina, làm sao nó biết đó là một sự kiện khi bạn nói d.MyEvent = null;, hay nó không?
Shimmy Weitzhandler

20
Có thể tôi đang thiếu một cái gì đó, nhưng đây không phải là sự kiện - đây là một thuộc tính đơn giản thuộc loại đại biểu.
Serge Berezovskiy

7
Khối đầu tiên có thể được viết bằng các loại ẩn danh: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);Tôi thấy điều này dễ đọc hơn nhưng ymmv. Và được đưa vào một cách tĩnh, nó hữu ích hơn trong bối cảnh này.
nawfal

13
@nawfal điều đó không đúng - ẩn danh khác với Expando. Bạn đang tạo một loại ẩn danh, sau đó không thể thêm các thuộc tính tùy ý vào.
Tiến sĩ Blowhard

75

Một lợi thế là cho các kịch bản ràng buộc. Lưới dữ liệu và lưới thuộc tính sẽ nhận các thuộc tính động thông qua hệ thống TypeDescriptor. Ngoài ra, liên kết dữ liệu WPF sẽ hiểu các thuộc tính động, vì vậy các điều khiển WPF có thể liên kết với ExpandoObject dễ dàng hơn từ điển.

Khả năng tương tác với các ngôn ngữ động, sẽ mong đợi các thuộc tính DLR thay vì các mục từ điển, cũng có thể được xem xét trong một số tình huống.


6
vẻ như cơ sở dữ liệu cho các đối tượng động bị hỏng . Người dùng báo cáo eisenbergeffect có mặt ở đây trên SO và điều phối viên của caliburn.micro. @AlexandraRusina bạn có thể nhận xét về trạng thái của lỗi và trạng thái "Không sửa"
lướt web vào

2
Đối với những người tò mò, tôi hiện có thể liên kết List<dynamic>IEnumerable<dynamic>sử dụng WPF4
Graham Bass

47

Lợi ích thực sự đối với tôi là ràng buộc dữ liệu hoàn toàn dễ dàng từ XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

 <TextBlock Text="{Binding SomeData.WhatEver}" />

28

Tương tác với các ngôn ngữ khác được thành lập trên DLRlà lý do số 1 tôi có thể nghĩ đến. Bạn không thể vượt qua họ Dictionary<string, object>vì nó không phải là một IDynamicMetaObjectProvider. Một lợi ích bổ sung khác là nó thực hiện INotifyPropertyChangedđiều đó có nghĩa là trong thế giới dữ liệu của WPF, nó cũng có thêm các lợi ích ngoài những gì Dictionary<K,V>có thể cung cấp cho bạn.


19

Đó là tất cả về sự tiện lợi của lập trình viên. Tôi có thể tưởng tượng việc viết các chương trình nhanh và bẩn với đối tượng này.


9
@J. Hendrix, đừng quên rằng anh ta cũng nói "bẩn". Intellisense có nhược điểm của nó, tuy nhiên, nó làm cho việc gỡ lỗi và bắt lỗi dễ dàng hơn. Cá nhân tôi vẫn thích các kiểu tĩnh hơn các kiểu động trừ khi tôi xử lý một trường hợp kỳ lạ (và luôn luôn hiếm).
Phil

+1 để thuận tiện. Tuy nhiên tôi thấy các loại ẩn danh có thể tiện lợi như một túi tài sản đơn giản và tốt hơn cho tính năng tĩnh của nó.
nawfal

1
Tôi sẽ không muốn sử dụng nó trong mã sản xuất, nhưng nó rất thuận tiện trong mã thử nghiệm và có thể làm cho nó trông rất đẹp.
Tobias

14

Tôi nghĩ rằng nó sẽ có lợi ích cú pháp, vì bạn sẽ không còn "giả mạo" các thuộc tính được thêm động bằng cách sử dụng từ điển.

Điều đó, và giao thoa với các ngôn ngữ động tôi sẽ nghĩ.


11

Đó là ví dụ từ bài viết tuyệt vời của MSDN về việc sử dụng ExpandoObject để tạo các loại quảng cáo động cho dữ liệu có cấu trúc đến (ví dụ XML, Json).

Chúng tôi cũng có thể chỉ định đại biểu cho thuộc tính động của ExpandoObject :

dynamic person = new ExpandoObject();
person.FirstName = "Dino";
person.LastName = "Esposito";

person.GetFullName = (Func<String>)(() => { 
  return String.Format("{0}, {1}", 
    person.LastName, person.FirstName); 
});

var name = person.GetFullName();
Console.WriteLine(name);

Do đó, nó cho phép chúng ta đưa một số logic vào đối tượng động khi chạy. Do đó, cùng với các biểu thức lambda, bao đóng, từ khóa động và lớp DynamicObject , chúng tôi có thể giới thiệu một số yếu tố của lập trình chức năng vào mã C # mà chúng tôi biết từ các ngôn ngữ động như JavaScript hoặc PHP.


4

Có một số trường hợp này là tiện dụng. Tôi sẽ sử dụng nó cho một vỏ Modularized chẳng hạn. Mỗi mô-đun xác định Hộp thoại Cấu hình riêng được lưu trữ theo các cài đặt của nó. Tôi cung cấp cho nó một ExpandoObject vì nó là Datacontext và lưu các giá trị trong Bộ lưu trữ cấu hình của tôi. Bằng cách này, trình soạn thảo Hộp thoại Cấu hình chỉ cần Liên kết với một Giá trị và nó tự động được tạo và lưu. (Và được cung cấp cho mô-đun để sử dụng các cài đặt này trong khóa học)

Nó đơn giản là dễ sử dụng hơn Từ điển. Nhưng mọi người nên biết rằng bên trong nó chỉ là một cuốn Từ điển.

Nó giống như LINQ chỉ là cú pháp đường, nhưng đôi khi nó làm cho mọi thứ dễ dàng hơn.

Vì vậy, để trả lời câu hỏi của bạn trực tiếp: Nó dễ viết hơn và dễ đọc hơn. Nhưng về mặt kỹ thuật, nó thực chất là một Dictionary<string,object>(Bạn thậm chí có thể bỏ nó thành một để liệt kê các giá trị).


-1
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

Tôi nghĩ rằng nó chỉ hoạt động vì mọi thứ đều có ToString (), nếu không, bạn phải biết loại đó và tạo 'đối tượng' cho loại đó.


Một số trong số này hữu ích thường xuyên hơn những cái khác, tôi đang cố gắng thấu đáo.

  1. Có thể tự nhiên hơn nhiều khi truy cập vào một bộ sưu tập, trong trường hợp này thực sự là một "từ điển", sử dụng ký hiệu dấu chấm trực tiếp hơn.

  2. Có vẻ như điều này có thể được sử dụng như một Tuple thực sự tốt đẹp. Bạn vẫn có thể gọi các thành viên của mình là "Item1", "Item2", v.v ... nhưng bây giờ bạn không phải, nó cũng có thể thay đổi, không giống như một Tuple. Điều này có nhược điểm rất lớn là thiếu sự hỗ trợ của intellisense.

  3. Bạn có thể không thoải mái với "tên thành viên dưới dạng chuỗi", như cảm nhận với từ điển, bạn có thể cảm thấy nó giống như "thực thi chuỗi", và nó có thể dẫn đến các quy ước đặt tên được mã hóa và xử lý làm việc với các hình thái và âm tiết khi mã đang cố hiểu cách sử dụng các thành viên :-P

  4. Bạn có thể gán giá trị cho chính ExpandoObject hay chỉ là thành viên? So sánh và tương phản với động / động [], sử dụng bất cứ điều gì phù hợp nhất với nhu cầu của bạn.

  5. Tôi không nghĩ động / động [] hoạt động trong vòng lặp foreach, bạn phải sử dụng var, nhưng có thể bạn có thể sử dụng ExpandoObject.

  6. Bạn không thể sử dụng động như một thành viên dữ liệu trong một lớp, có lẽ vì nó ít nhất giống như một từ khóa, hy vọng bạn có thể với ExpandoObject.

  7. Tôi hy vọng nó "là" một ExpandoObject, có thể hữu ích để gắn nhãn những thứ rất chung chung, với mã phân biệt dựa trên các loại có nhiều công cụ động được sử dụng.


Hãy tốt đẹp nếu bạn có thể đi sâu vào nhiều cấp độ cùng một lúc.

var e = new ExpandoObject();
e.position.x = 5;
etc...

Đó không phải là ví dụ tốt nhất có thể, hãy tưởng tượng sử dụng thanh lịch là phù hợp trong các dự án của riêng bạn.

Thật xấu hổ khi bạn không thể có mã xây dựng một số trong số này và đẩy kết quả đến mức không thể tin được. Tôi không chắc làm thế nào điều này sẽ làm việc mặc dù.

Hãy tốt đẹp nếu họ có thể có một giá trị cũng như các thành viên.

var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...

-3

Sau valueTuples, việc sử dụng lớp ExpandoObject là gì? mã 6 dòng này với ExpandoObject:

dynamic T = new ExpandoObject();
T.x = 1;
T.y = 2;
T.z = new ExpandoObject();
T.z.a = 3;
T.b= 4;

có thể được viết trong một dòng với bộ dữ liệu:

var T = (x: 1, y: 2, z: (a: 3, b: 4));

ngoài ra với cú pháp tuple, bạn có kiểu suy luận mạnh mẽ và hỗ trợ intlisense


1
Các ví dụ của bạn không giống nhau theo nghĩa là với giá trị tuple, bạn không thể viết Tc = 5; sau khi kết thúc xác định T. Với ExpandoObject bạn có thể làm điều đó bởi vì nó năng động. Ví dụ của bạn với giá trị tuple rất giống với khai báo kiểu ẩn danh. Ví dụ: var T2 = new {x = 1, y = 2, z = new {a = 3, b = 4}};
LxL

Tại sao tôi cần phải viết Tc = 5 mà không xác định nó? ExpandoObject chỉ hữu ích khi xử lý các đối tượng COM không phải là defiend trong .net. Mặt khác, tôi không sử dụng ExpandoObject này, vì nó bẩn và lỗi trong cả thời gian thiết kế và thời gian chạy.
Anh. M.Hamdy

1
Làm thế nào về bạn có z lúc đầu được gán cho (a: 3, b: 4) và sau đó bạn muốn z có thêm thuộc tính c? Bạn có thể làm điều đó với giá trị tuple?
LxL

Vì vậy, quan điểm của tôi là bạn không thể so sánh ExpandoObject với giá trị tuple vì chúng được thiết kế cho các mục đích khác nhau. Bằng cách so sánh theo cách của bạn, bạn đã loại bỏ chức năng mà ExpandoObject được thiết kế cho, đó là cấu trúc động.
LxL
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.