ความแตกต่างระหว่าง ExpandoObject, DynamicObject และไดนามิก


170

อะไรคือความแตกต่างระหว่างSystem.Dynamic.ExpandoObject, System.Dynamic.DynamicObjectและdynamic?

คุณใช้สถานการณ์ประเภทนี้ในสถานการณ์ใด

คำตอบ:


154

dynamicคำหลักที่จะใช้ในการประกาศตัวแปรที่ควรจะปลายที่ถูกผูกไว้
หากคุณต้องการใช้การรวมล่าช้าสำหรับประเภทจริงหรือจินตนาการคุณใช้dynamicคำหลักและคอมไพเลอร์ทำส่วนที่เหลือ

เมื่อคุณใช้dynamicคำหลักเพื่อโต้ตอบกับอินสแตนซ์ปกติDLR จะทำการโทรข้ามขอบไปยังวิธีปกติของอินสแตนซ์

IDynamicMetaObjectProviderอินเตอร์เฟซที่ช่วยให้ชั้นเรียนที่จะควบคุมพฤติกรรมของขอบล่าช้าของ
เมื่อคุณใช้dynamicคีย์เวิร์ดเพื่อโต้ตอบกับIDynamicMetaObjectProviderการนำไปใช้งาน DLR จะเรียกIDynamicMetaObjectProviderเมธอดและวัตถุเองจะตัดสินใจว่าจะทำอย่างไร

ExpandoObjectและเรียนการใช้งานของDynamicObjectIDynamicMetaObjectProvider

ExpandoObjectเป็นคลาสแบบง่ายที่ช่วยให้คุณเพิ่มสมาชิกในอินสแตนซ์และใช้พวกเขาdynamicเป็นพันธมิตร
DynamicObjectเป็นการใช้งานขั้นสูงเพิ่มเติมซึ่งสามารถสืบทอดเพื่อให้พฤติกรรมที่กำหนดเองได้อย่างง่ายดาย


2
อะไรจะเป็นสถานที่ที่ดีในการเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้? ไม่ใช่ API แต่เป็นเพราะอะไรที่อยู่เบื้องหลัง API เช่นเหตุใด ExpandoObject จึงไม่ได้มาจาก DynamicObject ซึ่งดูประเภทพื้นฐานของ defacto สำหรับการเขียนโปรแกรมพื้นฐานของ ruby ​​'method_missing'
Gishu

4
คุณสามารถเพิ่มตัวอย่างการใช้งานที่เป็นไปได้? ตัวอย่างเช่นฉันจะใช้ DynamicObject ได้อย่างไรและมีประโยชน์อย่างไร
oɔɯǝɹ

10
คำตอบที่ยอดเยี่ยมหากไม่มีตัวอย่างเช่นนี้เป็นเหมือนเค้กที่ไม่มีครีมอยู่ด้านบน
Teoman shipahi


68

ฉันจะพยายามให้คำตอบที่ชัดเจนสำหรับคำถามนี้เพื่ออธิบายอย่างชัดเจนว่าความแตกต่างระหว่างไดนามิกExpandoObjectและDynamicObjectอะไร

เร็วมากdynamicเป็นคำหลัก มันไม่ได้เป็นประเภทต่อ se มันเป็นคำหลักที่บอกคอมไพเลอร์ที่จะละเว้นการตรวจสอบประเภทคงที่ในขณะออกแบบและแทนที่จะใช้สายผูกพันในเวลาทำงาน ดังนั้นเราจะไม่ใช้เวลามากdynamicในการตอบคำถามนี้

ExpandoObjectและDynamicObjectเป็นประเภทแน่นอน บนพื้นผิวพวกเขาดูคล้ายกันมาก IDynamicMetaObjectProviderทั้งชั้นเรียนใช้ อย่างไรก็ตามขุดลึกลงไปแล้วคุณจะพบว่ามันไม่เหมือนกันเลย

DynamicObject เป็นการใช้งานบางส่วนของIDynamicMetaObjectProviderความหมายล้วนๆเพื่อเป็นจุดเริ่มต้นสำหรับนักพัฒนาในการใช้ประเภทที่กำหนดเองของพวกเขาเองสนับสนุนการจัดส่งแบบไดนามิกที่มีการจัดเก็บข้อมูลพื้นฐานที่กำหนดเองและพฤติกรรมการดึงเพื่อให้การทำงานแบบไดนามิก

  1. DynamicObject ไม่สามารถสร้างได้โดยตรง
  2. คุณต้องขยาย DynamicObject เพื่อให้มีประโยชน์กับคุณในฐานะนักพัฒนา
  3. เมื่อคุณขยาย DynamicObject ตอนนี้คุณสามารถกำหนดลักษณะการทำงานที่กำหนดเองเกี่ยวกับวิธีที่คุณต้องการให้การจัดส่งแบบไดนามิกเพื่อแก้ไขข้อมูลที่เก็บไว้ภายในในการแสดงข้อมูลพื้นฐานของคุณในเวลาทำงาน
  4. ExpandoObject จัดเก็บข้อมูลพื้นฐานในพจนานุกรม ฯลฯ หากคุณใช้ DynamicObject คุณสามารถจัดเก็บข้อมูลได้ทุกที่และทุกเวลาที่คุณต้องการ (เช่นวิธีที่คุณได้รับและตั้งค่าข้อมูลการจัดส่งนั้นขึ้นอยู่กับคุณทั้งหมด)

กล่าวโดยย่อให้ใช้ DynamicObject เมื่อคุณต้องการสร้างประเภทของคุณเองที่สามารถใช้กับ DLR และทำงานกับพฤติกรรมที่กำหนดเองใด ๆ ที่คุณต้องการ

ตัวอย่าง: ลองนึกภาพว่าคุณต้องการมีประเภทแบบไดนามิกที่ส่งกลับค่าเริ่มต้นที่กำหนดเองเมื่อใดก็ตามที่มีการพยายามรับสมาชิกที่ไม่มีอยู่ (เช่นไม่ได้ถูกเพิ่มในขณะใช้งาน) และค่าเริ่มต้นนั้นจะบอกว่า "ฉันขอโทษไม่มีคุกกี้ในขวดนี้!" หากคุณต้องการวัตถุแบบไดนามิกที่ทำงานเช่นนี้คุณจะต้องควบคุมสิ่งที่เกิดขึ้นเมื่อไม่พบเขตข้อมูล ExpandoObject จะไม่ยอมให้คุณทำเช่นนี้ ดังนั้นคุณจะต้องสร้างรูปแบบของคุณเองด้วยที่ไม่ซ้ำกันความละเอียดสมาชิกแบบไดนามิก (ส่ง) ExpandoObjectพฤติกรรมและการใช้งานที่แทนตัวพร้อม

คุณสามารถสร้างประเภทดังต่อไปนี้: (หมายเหตุรหัสด้านล่างเป็นเพียงภาพประกอบและอาจไม่ทำงานหากต้องการเรียนรู้เกี่ยวกับวิธีการใช้ DynamicObject อย่างถูกต้องมีบทความและบทแนะนำมากมายที่อื่น)

public class MyNoCookiesInTheJarDynamicObject : DynamicObject
{
    Dictionary<string, object> properties = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (properties.ContainsKey(binder.Name))
        {
            result = properties[binder.Name];
            return true;
        }
        else
        {
            result = "I'm sorry, there are no cookies in this jar!"; //<-- THIS IS OUR 
            CUSTOM "NO COOKIES IN THE JAR" RESPONSE FROM OUR DYNAMIC TYPE WHEN AN UNKNOWN FIELD IS ACCESSED
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        properties[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        dynamic method = properties[binder.Name];
        result = method(args[0].ToString(), args[1].ToString());
        return true;
    }
}

ทีนี้เราสามารถใช้คลาสจินตภาพนี้ที่เราเพิ่งสร้างเป็นแบบไดนามิกที่มีพฤติกรรมที่กำหนดเองมากถ้าไม่มีฟิลด์

dynamic d = new MyNoCookiesInTheJarDynamicObject();
var s = d.FieldThatDoesntExist;

//in our contrived example, the below should evaluate to true
Assert.IsTrue(s == "I'm sorry, there are no cookies in this jar!")

ExpandoObjectเป็นการใช้งานแบบเต็มรูปแบบIDynamicMetaObjectProviderซึ่งทีมงาน. NET Framework ได้ทำการตัดสินใจทั้งหมดสำหรับคุณแล้ว สิ่งนี้มีประโยชน์หากคุณไม่ต้องการพฤติกรรมที่กำหนดเองใด ๆ และคุณรู้สึกว่า ExpandoObject ใช้ได้ดีพอสำหรับคุณ (90% ของเวลาExpandoObjectนั้นดีพอ) ตัวอย่างเช่นดูต่อไปนี้และสำหรับ ExpandoObject ผู้ออกแบบเลือกที่จะโยนข้อยกเว้นหากไม่มีสมาชิกแบบไดนามิก

dynamic d = new ExpandoObject();

/*
The ExpandoObject designers chose that this operation should result in an 
Exception. They did not have to make that choice, null could 
have been returned, for example; or the designers could've returned a "sorry no cookies in the jar" response like in our custom class. However, if you choose to use 
ExpandoObject, you have chosen to go with their particular implementation 
of DynamicObject behavior.
*/

try {
var s = d.FieldThatDoesntExist;
}
catch(RuntimeBinderException) { ... }

ดังนั้นการสรุปExpandoObjectจึงเป็นเพียงวิธีหนึ่งที่เลือกไว้ล่วงหน้าเพื่อขยาย DynamicObject ด้วยพฤติกรรมการจัดส่งแบบไดนามิกบางอย่างที่อาจเป็นไปได้สำหรับคุณแต่อาจไม่ขึ้นอยู่กับความต้องการเฉพาะของคุณ

ในขณะที่DyanmicObjectเป็นผู้ช่วย BaseType ที่ทำให้การใช้งานประเภทของคุณเองด้วยพฤติกรรมแบบไดนามิกที่ไม่ซ้ำกันง่ายและสะดวก

บทช่วยสอนที่มีประโยชน์ซึ่งมีแหล่งตัวอย่างส่วนใหญ่ด้านบน


คำอธิบายที่ดีมาก เพียงแค่การแก้ไขทางเทคนิคเดียว: ExpandoObject ไม่ได้สืบทอดจาก DynamicObject
Mike Rosoft

การแก้ไขเล็กน้อยในตัวอย่างสำหรับDynamicObject: เมื่อแทนที่TryGetMemberหากคุณส่งคืนค่าเท็จRuntimeBinderExceptionจะถูกโยนทิ้งเมื่อพยายามเข้าถึงคุณสมบัติที่ไม่มีอยู่ trueสำหรับข้อมูลโค้ดในการทำงานจริงที่คุณควรจะกลับ
lluchmk

36

ตามข้อกำหนดภาษา C # dynamicเป็นการประกาศประเภท คือdynamic xหมายความว่าตัวแปรที่มีชนิดxdynamic

DynamicObjectเป็นประเภทที่ทำให้ง่ายต่อการใช้งานIDynamicMetaObjectProviderและดังนั้นจึงแทนที่พฤติกรรมการผูกเฉพาะสำหรับประเภท

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


25
dynamicไม่ใช่ประเภทที่แท้จริง ... เป็นเพียงคำแนะนำในการบอกคอมไพเลอร์ให้ใช้การรวมภายหลังสำหรับตัวแปรนี้ dynamicตัวแปรถูกประกาศตามจริงobjectใน MSIL
Thomas Levesque

1
@Thomas: จากมุมมองของคอมไพเลอร์มันเป็นประเภท แต่คุณพูดถูกว่าการแสดงแบบ runtime นั้นเป็นของ Object คุณจะพบคำสั่ง "พิมพ์แบบคงที่ให้เป็นแบบไดนามิก" ในงานนำเสนอ MS หลายรายการ
Brian Rasmussen

3
@Thomas: และข้อกำหนดภาษาบอกว่า "C # 4.0 แนะนำประเภทคงที่ใหม่ที่เรียกว่าไดนามิก"
Brian Rasmussen

แน่นอน ... แต่ฉันคิดว่ามันสับสนที่จะพิจารณาว่าเป็นประเภทเนื่องจากไม่มีความสัมพันธ์ทางมรดกกับประเภทเช่น DynamicObject หรือ ExpandoObject
Thomas Levesque

3
@NathanA ฉันอยู่กับคุณที่นี่ อย่างไรก็ตามสเปคภาษาเรียกมันว่าเป็นประเภทดังนั้นนั่นคือสิ่งที่ฉันจะไปด้วย
Brian Rasmussen

0

ตัวอย่างข้างต้นของการไม่บอกความแตกต่างอย่างเห็นได้ชัดเพราะมันเป็นพื้นการใช้ฟังก์ชั่นที่มีให้แล้วโดยDynamicObjectExpandoObject

ในลิงก์ทั้งสองที่กล่าวถึงด้านล่างมีความชัดเจนมากว่าด้วยความช่วยเหลือของDynamicObjectมันเป็นไปได้ที่จะรักษา / เปลี่ยนประเภทที่แท้จริง ( XElementในตัวอย่างที่ใช้ในลิงค์ด้านล่าง) และควบคุมคุณสมบัติและวิธีการต่างๆได้ดีขึ้น

https://blogs.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-introducing-the-expandoobject/

https://blogs.msdn.microsoft.com/csharpfaq/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject/

public class DynamicXMLNode : DynamicObject    
{    
    XElement node;

    public DynamicXMLNode(XElement node)    
    {    
        this.node = node;    
    }

    public DynamicXMLNode()    
    {    
    }

    public DynamicXMLNode(String name)    
    {    
        node = new XElement(name);    
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)    
    {    
        XElement setNode = node.Element(binder.Name);

        if (setNode != null)    
            setNode.SetValue(value);    
        else    
        {    
            if (value.GetType() == typeof(DynamicXMLNode))    
                node.Add(new XElement(binder.Name));    
            else    
                node.Add(new XElement(binder.Name, value));    
        }

        return true;    
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)    
    {    
        XElement getNode = node.Element(binder.Name);

        if (getNode != null)    
        {    
            result = new DynamicXMLNode(getNode);    
            return true;    
        }    
        else    
        {    
            result = null;    
            return false;    
        }    
    }    
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.