ประโยชน์ที่แท้จริงของ ExpandoObject คืออะไร


587

ExpandoObjectระดับที่เพิ่มเข้ามา .NET 4 ช่วยให้คุณสามารถตั้งค่าคุณสมบัติพลลงบนวัตถุที่รันไทม์

มีข้อได้เปรียบอะไรบ้างในการใช้ a Dictionary<string, object>หรือแม้กระทั่งHashtableจริงๆ? เท่าที่ฉันสามารถบอกได้นี่คืออะไร แต่เป็นตารางแฮชที่คุณสามารถเข้าถึงได้ด้วยไวยากรณ์ที่กระชับขึ้นเล็กน้อย

ตัวอย่างเช่นทำไมจึงเป็นเช่นนี้:

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

ดีกว่าจริงๆหรือแตกต่างอย่างมีนัยสำคัญกว่า:

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

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

อะไรจริงข้อดีจะได้รับจากการใช้ ExpandoObject แทนเพียงใช้โดยพลการประเภทพจนานุกรมอื่น ๆ กว่าไม่เป็นที่ชัดเจนว่าคุณกำลังใช้ชนิดที่ว่าจะได้รับการพิจารณาที่รันไทม์

คำตอบ:


689

ตั้งแต่ฉันเขียนบทความ MSDN ที่คุณอ้างถึงฉันคิดว่าฉันต้องตอบคำถามนี้

ครั้งแรกฉันคาดหวังคำถามนี้และนั่นเป็นสาเหตุที่ฉันเขียนโพสต์บล็อกที่แสดงกรณีการใช้งานจริงมากขึ้นหรือน้อยลงสำหรับ ExpandoObject: ไดนามิกใน C # 4.0: แนะนำ ExpandoObjectExpandoObject

ในไม่ช้า ExpandoObject สามารถช่วยคุณสร้างวัตถุลำดับชั้นที่ซับซ้อนได้ ตัวอย่างเช่นสมมติว่าคุณมีพจนานุกรมอยู่ในพจนานุกรม:

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"]);

ยิ่งลึกลงไปคือลำดับชั้น uglier คือรหัส ด้วย ExpandoObject มันจะยังคงสง่างามและสามารถอ่านได้

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

ข้อสองตามที่ชี้แจงแล้ว ExpandoObject ใช้อินเทอร์เฟซ INotifyPropertyChanged ซึ่งให้การควบคุมคุณสมบัติมากกว่าพจนานุกรม

ในที่สุดคุณสามารถเพิ่มกิจกรรมไปยัง ExpandoObject ได้ที่นี่:

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);
   }
}

นอกจากนี้โปรดทราบว่าไม่มีสิ่งใดขัดขวางคุณจากการยอมรับการขัดแย้งของเหตุการณ์ในลักษณะที่เปลี่ยนแปลง ในคำอื่น ๆ แทนการใช้EventHandlerคุณสามารถใช้ซึ่งจะทำให้เกิดข้อโต้แย้งที่สองของการจัดการที่จะเป็นEventHandler<dynamic>dynamic


53
น่าสนใจ ขอบคุณสำหรับข้อมูลใหม่: กิจกรรม นั่นคือใหม่สำหรับฉัน
Reed Copsey

16
@AlexandraRusina คุณรู้ได้อย่างไรว่าเป็นกิจกรรมเมื่อคุณพูดd.MyEvent = null;หรือไม่?
Shimmy Weitzhandler

20
บางทีฉันอาจจะพลาดบางสิ่งบางอย่าง แต่นี่ไม่ใช่เหตุการณ์ - นี่เป็นคุณสมบัติแบบธรรมดาของประเภทตัวแทน
Sergey Berezovskiy

7
บล็อกแรกสามารถเขียนได้โดยใช้ประเภทที่ไม่ระบุตัวตน: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);ฉันพบว่าสามารถอ่านได้มากกว่านี้ แต่ ymmv และเนื่องจากมันถูกพิมพ์แบบคงที่จึงมีประโยชน์มากกว่าในบริบทนี้
nawfal

13
@nawfal ไม่ถูกต้อง - ผู้ไม่ระบุชื่อนั้นแตกต่างจาก Expando คุณกำลังสร้างประเภทนิรนามซึ่งไม่สามารถเพิ่มคุณสมบัติตามอำเภอใจได้
ดร. โบลฮาร์ด

75

ข้อดีอย่างหนึ่งคือสำหรับสถานการณ์ที่มีผลผูกพัน กริดข้อมูลและกริดคุณสมบัติจะรับคุณสมบัติไดนามิกผ่านระบบ TypeDescriptor นอกจากนี้การเชื่อมโยงข้อมูล WPF จะเข้าใจคุณสมบัติแบบไดนามิกดังนั้นการควบคุม WPF สามารถผูกกับ ExpandoObject ได้ง่ายกว่าพจนานุกรม

ความสามารถในการทำงานร่วมกันกับภาษาแบบไดนามิกซึ่งคาดว่าจะมีคุณสมบัติของ DLR มากกว่ารายการพจนานุกรมอาจพิจารณาในบางสถานการณ์


6
มันดูเหมือนว่า databinding วัตถุแบบไดนามิกที่เสีย ผู้ใช้รายงาน eisenbergeffect อยู่ที่นี่บน SO และผู้ประสานงานของ caliburn.micro @AlexandraRusina คุณสามารถแสดงความคิดเห็นเกี่ยวกับสถานะของข้อผิดพลาดและสถานะ "จะไม่แก้ไข"
surfmuggle

2
สำหรับผู้ที่อยากรู้อยากเห็นฉันสามารถผูกList<dynamic>และIEnumerable<dynamic>ใช้ WPF4
Graham Bass

47

ประโยชน์ที่แท้จริงสำหรับฉันคือการเชื่อมโยงข้อมูลที่ง่ายดายจาก XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

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

28

การทำงานร่วมกันกับภาษาอื่น ๆ ที่ก่อตั้งขึ้นในครั้งDLRนี้คือเหตุผล # 1 คุณไม่สามารถส่งพวกเขาในขณะที่มันไม่ได้เป็นDictionary<string, object> IDynamicMetaObjectProviderประโยชน์เพิ่มเติมอีกประการหนึ่งคือการใช้งานINotifyPropertyChangedซึ่งหมายความว่าในโลกของฐานข้อมูลของ WPF มันยังได้เพิ่มประโยชน์นอกเหนือจากสิ่งที่Dictionary<K,V>สามารถให้คุณ


19

มันคือทั้งหมดที่เกี่ยวกับความสะดวกสบายของโปรแกรมเมอร์ ฉันสามารถจินตนาการการเขียนโปรแกรมที่รวดเร็วและสกปรกด้วยวัตถุนี้


9
@J เฮนดริกซ์อย่าลืมว่าเขายังพูดว่า "สกปรก" Intellisense มีข้อเสีย แต่ก็ทำให้การดีบักและจับข้อผิดพลาดง่ายขึ้น ฉันยังคงชอบแบบคงที่มากกว่าประเภทแบบไดนามิกเว้นแต่ฉันจะจัดการกับกรณีแปลก ๆ (และหายากเสมอ)
Phil

+1 เพื่อความสะดวก อย่างไรก็ตามฉันพบว่าประเภทที่ไม่ระบุชื่อสามารถใช้งานได้อย่างสะดวกสบายในรูปแบบกระเป๋าแบบง่าย ๆ และดีกว่าสำหรับรูปแบบคงที่
nawfal

1
ฉันไม่ต้องการใช้ในโค้ดการผลิต แต่สะดวกในการทดสอบโค้ดและสามารถทำให้มันดูสวยงามมาก
โทเบียส

14

ฉันคิดว่ามันจะมีประโยชน์ทางไวยากรณ์เนื่องจากคุณจะไม่ "ปลอม" คุณสมบัติที่เพิ่มขึ้นแบบไดนามิกโดยใช้พจนานุกรม

และฉันก็คิดว่าการทำงานร่วมกับภาษาแบบไดนามิก


11

เป็นตัวอย่างจากบทความ MSDNที่ยอดเยี่ยมเกี่ยวกับการใช้ExpandoObjectสำหรับการสร้างประเภทโฆษณาแบบไดนามิกสำหรับข้อมูลที่มีโครงสร้างเข้ามา (เช่น XML, Json)

นอกจากนี้เรายังสามารถมอบหมายตัวแทนให้กับทรัพย์สินแบบไดนามิกของ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);

ดังนั้นมันช่วยให้เราสามารถฉีดตรรกะบางอย่างลงในวัตถุแบบไดนามิกที่รันไทม์ ดังนั้นเมื่อรวมกับการแสดงออกแลมบ์ดาการปิดคำสำคัญแบบไดนามิกและคลาส DynamicObjectเราสามารถแนะนำองค์ประกอบบางส่วนของการเขียนโปรแกรมการทำงานลงในรหัส C # ซึ่งเรารู้จากภาษาแบบไดนามิกเช่น JavaScript หรือ PHP


4

มีบางกรณีที่สิ่งนี้มีประโยชน์ ฉันจะใช้มันสำหรับเชลล์แบบโมดูล แต่ละโมดูลกำหนดว่าเป็นกล่องโต้ตอบการกำหนดค่าของตัวเอง ฉันให้กับ ExpandoObject เนื่องจากเป็น Datacontext และบันทึกค่าในที่เก็บข้อมูลการกำหนดค่าของฉัน วิธีนี้นักเขียนการกำหนดค่าจะต้องผูกกับค่าและสร้างและบันทึกโดยอัตโนมัติ (และจัดให้กับโมดูลสำหรับการใช้การตั้งค่าเหล่านี้แน่นอน)

ใช้ง่ายกว่าพจนานุกรม แต่ทุกคนควรตระหนักว่าภายในนั้นเป็นเพียงพจนานุกรม

มันเหมือนกับ LINQ แค่น้ำตาลทราย แต่มันทำให้บางครั้งง่ายขึ้น

ดังนั้นเพื่อตอบคำถามของคุณโดยตรง: ง่ายต่อการเขียนและอ่านง่ายขึ้น แต่ในทางเทคนิคแล้วมันก็คือDictionary<string,object>(คุณสามารถโยนมันลงในรายการเดียวเพื่อแสดงค่า)


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

ฉันคิดว่าใช้งานได้เพราะทุกอย่างมี ToString () ไม่เช่นนั้นคุณต้องรู้ว่ามันเป็นแบบไหนและโยน 'วัตถุ' ไปเป็นประเภทนั้น


สิ่งเหล่านี้มีประโยชน์มากกว่าคนอื่น ๆ ฉันพยายามที่จะละเอียดถี่ถ้วน

  1. อาจเป็นเรื่องธรรมดากว่าที่จะเข้าถึงคอลเล็กชั่นในกรณีนี้ "พจนานุกรม" ที่มีประสิทธิภาพคืออะไรโดยใช้เครื่องหมายจุดตรงมากขึ้น

  2. ดูเหมือนว่าสิ่งนี้สามารถใช้เป็น Tuple ที่ดีจริงๆ คุณยังสามารถเรียกสมาชิกของคุณ "Item1", "Item2" ฯลฯ ... แต่ตอนนี้คุณไม่จำเป็นต้องทำมันยังเปลี่ยนแปลงไม่ได้เหมือน Tuple สิ่งนี้จะมีข้อเสียเปรียบอย่างมากจากการขาดการสนับสนุนภายใน

  3. คุณอาจรู้สึกไม่สบายใจกับ "ชื่อสมาชิกเป็นสายอักขระ" เช่นเดียวกับความรู้สึกในพจนานุกรมคุณอาจรู้สึกว่ามันเป็นเหมือน "การเรียกใช้สตริง" และอาจนำไปสู่การตั้งชื่อการประชุมที่ได้รับรหัสและการทำงานกับ morphemes และ พยางค์เมื่อรหัสพยายามเข้าใจวิธีใช้สมาชิก :-P

  4. คุณสามารถกำหนดค่าให้กับ ExpandoObject เองหรือแค่เป็นสมาชิกได้หรือไม่? เปรียบเทียบและความคมชัดด้วยไดนามิก / ไดนามิก [] ให้ใช้ตามความต้องการของคุณมากที่สุด

  5. ฉันไม่คิดว่า dynamic / dynamic [] ทำงานในลูป foreach คุณต้องใช้ var แต่อาจเป็นไปได้ว่าคุณสามารถใช้ ExpandoObject

  6. คุณไม่สามารถใช้ไดนามิกเป็นสมาชิกข้อมูลในคลาสได้เนื่องจากอย่างน้อยมันก็เหมือนกับคำหลักหวังว่าคุณสามารถทำได้ด้วย ExpandoObject

  7. ฉันคาดหวังว่า "เป็น" ExpandoObject อาจเป็นประโยชน์ในการติดป้ายกำกับสิ่งที่แตกต่างกันออกไปมากโดยใช้รหัสที่แตกต่างกันไปตามประเภทที่มีการใช้สิ่งต่าง ๆ มากมาย


จะดีถ้าคุณสามารถเจาะลึกหลายระดับในครั้งเดียว

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

ไม่ใช่ตัวอย่างที่ดีที่สุดลองจินตนาการถึงการใช้อย่างหรูหราตามความเหมาะสมในโครงการของคุณ

เป็นเรื่องน่าละอายที่คุณไม่สามารถสร้างโค้ดเหล่านี้และส่งผลให้เกิดการ intellisense ฉันไม่แน่ใจว่ามันจะทำงานอย่างไร

เป็นคนดีถ้าพวกเขามีค่าเช่นเดียวกับสมาชิก

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

-3

หลังจาก valueTuples แล้วการใช้คลาส ExpandoObject คืออะไร รหัส 6 บรรทัดนี้พร้อม ExpandoObject:

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

สามารถเขียนในบรรทัดเดียวกับสิ่งอันดับ:

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

นอกจากนี้ยังมีไวยากรณ์ tuple คุณมีการอนุมานประเภทที่แข็งแกร่งและการสนับสนุน intlisense


1
ตัวอย่างของคุณไม่เหมือนกันในแง่ที่ว่าคุณค่าของสิ่งอันดับคุณไม่สามารถเขียน Tc = 5; หลังจากเสร็จสิ้นกำหนด T ด้วย ExpandoObject คุณสามารถทำได้เพราะมันเป็นแบบไดนามิก ตัวอย่างของคุณที่มี tuple ค่าเหมือนกันมากกับการประกาศประเภทไม่ระบุชื่อ เช่น: var T2 = new {x = 1, y = 2, z = new {a = 3, b = 4}};
LxL

ทำไมฉันต้องเขียน Tc = 5 โดยไม่ต้องกำหนด ExpandoObject ใช้ได้เฉพาะเมื่อจัดการกับวัตถุ COM ที่ไม่ได้ defiend ใน. net มิฉะนั้นฉันจะใช้ ExpandoObject นี้เพราะมันสกปรกและบั๊กกี้ทั้งในเวลาออกแบบและรันไทม์
อังกฤษ M.Hamdy

1
คุณมี z ในตอนแรกที่ได้รับมอบหมายให้ (a: 3, b: 4) แล้วหลังจากนั้นคุณอยากให้ z มีคุณสมบัติ c เพิ่มเติมหรือไม่ คุณสามารถทำได้ด้วย tuple ค่าหรือไม่?
LxL

ดังนั้นประเด็นของฉันคือคุณไม่สามารถเปรียบเทียบ ExpandoObject กับ value tuple ได้เพราะมันถูกออกแบบมาเพื่อจุดประสงค์ที่แตกต่างกัน ด้วยการเปรียบเทียบวิธีการของคุณคุณจะยกเลิกฟังก์ชั่นที่ ExpandoObject ได้รับการออกแบบมาซึ่งเป็นโครงสร้างแบบไดนามิก
LxL
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.