Sự khác biệt giữa ExpandoObject, DynamicObject và động


170

Sự khác biệt giữa System.Dynamic.ExpandoObject, System.Dynamic.DynamicObjectvà là dynamicgì?

Trong những tình huống bạn sử dụng các loại?

Câu trả lời:


154

Các dynamictừ khóa được sử dụng để khai báo các biến cần được cuối-bound.
Nếu bạn muốn sử dụng liên kết muộn, đối với bất kỳ loại thực hoặc tưởng tượng, bạn sử dụng dynamictừ khóa và trình biên dịch làm phần còn lại.

Khi bạn sử dụng dynamictừ khóa để tương tác với một cá thể bình thường, DLR thực hiện các cuộc gọi bị ràng buộc muộn đến các phương thức thông thường của cá thể.

Các IDynamicMetaObjectProvidergiao diện cho phép một lớp học để kiểm soát hành vi cuối-bound của nó.
Khi bạn sử dụng dynamictừ khóa để tương tác với một IDynamicMetaObjectProvidertriển khai, DLR gọi các IDynamicMetaObjectProviderphương thức và chính đối tượng quyết định phải làm gì.

Các lớp ExpandoObjectDynamicObjectđang triển khai IDynamicMetaObjectProvider.

ExpandoObjectlà một lớp đơn giản cho phép bạn thêm các thành viên vào một thể hiện và sử dụng chúng dynamicđồng minh.
DynamicObjectlà một triển khai nâng cao hơn có thể được kế thừa để dễ dàng cung cấp hành vi tùy chỉnh.


2
Điều gì sẽ là một nơi tốt để tìm hiểu thêm về điều này? Không phải API mà là lý do đằng sau API? ví dụ: Tại sao ExpandoObject không xuất phát từ DynamicObject, trông giống loại cơ sở defacto cho lập trình dựa trên 'method_missing' của ruby.
Gishu

4
Bạn có thể thêm một số ví dụ sử dụng khi có thể? Chẳng hạn, tôi sẽ sử dụng DynamicObject như thế nào và lợi ích là gì?
oɔɯǝɹ 30/12/14

10
Câu trả lời tuyệt vời mà không có ví dụ như thế này giống như bánh không có kem trên đầu.
Teoman shipahi


68

Tôi sẽ cố gắng cung cấp một câu trả lời rõ ràng hơn cho câu hỏi này, để giải thích rõ ràng sự khác biệt giữa động ExpandoObjectDynamicObject.

Rất nhanh chóng, dynamiclà một từ khóa. Nó không phải là một loại per-se. Đó là một từ khóa yêu cầu trình biên dịch bỏ qua kiểm tra kiểu tĩnh tại thời điểm thiết kế và thay vào đó sử dụng liên kết trễ vào thời gian chạy. Vì vậy, chúng tôi sẽ không dành nhiều thời gian dynamiccho phần còn lại của câu trả lời này.

ExpandoObjectDynamicObjectthực sự là các loại. Trên BỀ MẶT, chúng trông rất giống nhau. Cả hai lớp thực hiện IDynamicMetaObjectProvider. Tuy nhiên, đào sâu hơn và bạn sẽ thấy chúng KHÔNG giống nhau chút nào.

DynamicObject là một triển khai một phần IDynamicMetaObjectProviderhoàn toàn có nghĩa là điểm khởi đầu để các nhà phát triển triển khai các loại tùy chỉnh của riêng họ hỗ trợ công văn động với hành vi lưu trữ và truy xuất cơ sở tùy chỉnh để làm cho công văn động hoạt động.

  1. DynamicObject không thể được xây dựng trực tiếp.
  2. Bạn PHẢI mở rộng DynamicObject để nó có bất kỳ mục đích sử dụng nào với tư cách là nhà phát triển.
  3. Khi bạn mở rộng DynamicObject, giờ đây bạn có thể cung cấp hành vi TÙY CHỈNH về cách bạn muốn công văn động giải quyết dữ liệu được lưu trữ nội bộ trong biểu diễn dữ liệu cơ bản của bạn trong thời gian chạy.
  4. ExpandoObject lưu trữ dữ liệu cơ bản trong Từ điển, v.v. Nếu bạn triển khai DynamicObject, bạn có thể lưu trữ dữ liệu ở bất cứ đâu và theo cách bạn muốn. (ví dụ: cách bạn nhận và thiết lập dữ liệu gửi hoàn toàn tùy thuộc vào bạn).

Nói tóm lại, hãy sử dụng DynamicObject khi bạn muốn tạo các loại OWN có thể được sử dụng với DLR và hoạt động với bất kỳ hành vi TÙY CHỈ nào bạn muốn.

Ví dụ: Hãy tưởng tượng rằng bạn muốn có một loại động trả về mặc định tùy chỉnh bất cứ khi nào có được một thành viên KHÔNG tồn tại (tức là chưa được thêm vào lúc chạy). Và mặc định đó sẽ nói, "Tôi xin lỗi, không có cookie trong bình này!". Nếu bạn muốn một đối tượng động hoạt động như thế này, bạn sẽ cần kiểm soát những gì xảy ra khi không tìm thấy trường. ExpandoObject sẽ không cho phép bạn làm điều này. Vì vậy, bạn sẽ cần tạo loại của riêng mình với hành vi (gửi) thành viên động duy nhất và sử dụng loại đó thay vì được tạo sẵn ExpandoObject.

Bạn có thể tạo một loại như sau: (Lưu ý, đoạn mã dưới đây chỉ để minh họa và có thể không chạy. Để tìm hiểu về cách sử dụng DynamicObject đúng cách, có nhiều bài viết và hướng dẫn ở nơi khác.)

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

Bây giờ, chúng ta có thể sử dụng lớp tưởng tượng này mà chúng ta vừa tạo như một kiểu động có hành vi rất tùy chỉnh nếu trường không tồn tại.

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!")

ExpandoObjectlà một triển khai ĐẦY ĐỦ IDynamicMetaObjectProvider, trong đó nhóm .NET Framework đã đưa ra tất cả các quyết định này cho bạn. Điều này hữu ích nếu bạn không cần bất kỳ hành vi tùy chỉnh nào và bạn cảm thấy ExpandoObject hoạt động đủ tốt cho bạn (90% thời gian, ExpandoObjectlà đủ tốt). Vì vậy, ví dụ, xem phần sau đây và đối với ExpandoObject, các nhà thiết kế đã chọn đưa ra một ngoại lệ nếu thành viên động không tồn tại.

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) { ... }

Vì vậy, để tóm tắt, ExpandoObjectchỉ đơn giản là một cách được chọn trước để mở rộng DynamicObject với các hành vi điều phối động nhất định có thể sẽ phù hợp với bạn , nhưng có thể không phụ thuộc vào nhu cầu cụ thể của bạn.

Trong khi đó, DyanmicObjectlà một BaseType trợ giúp giúp thực hiện các kiểu của riêng bạn với các hành vi động độc đáo đơn giản và dễ dàng.

Một hướng dẫn hữu ích mà phần lớn nguồn ví dụ ở trên được dựa trên.


Giải thích rất tốt. Chỉ cần một hiệu chỉnh kỹ thuật: ExpandoObject không kế thừa từ DynamicObject.
Mike Rosoft

Một sửa chữa nhỏ trên ví dụ cho DynamicObject: khi ghi đè TryGetMember, nếu bạn trả về sai, RuntimeBinderExceptionsẽ bị ném khi cố gắng truy cập vào tài sản không tồn tại. Để đoạn trích thực sự hoạt động, bạn nên quay lại true.
lluchmk

36

Theo đặc tả ngôn ngữ C # dynamiclà một khai báo kiểu. Nghĩa dynamic xlà biến xcó kiểu dynamic.

DynamicObjectlà một loại giúp dễ dàng thực hiện IDynamicMetaObjectProvidervà do đó ghi đè hành vi ràng buộc cụ thể cho loại.

ExpandoObjectlà một loại hoạt động như một túi tài sản. Tức là bạn có thể thêm các thuộc tính, phương thức và vv cho các trường hợp động của loại này khi chạy.


25
dynamickhông phải là một loại thực tế ... nó chỉ là một gợi ý để nói cho trình biên dịch sử dụng liên kết muộn cho biến này. dynamiccác biến thực sự được khai báo như objecttrong MSIL
Thomas Levesque

1
@Thomas: theo quan điểm của nhà biên dịch, nó là một loại, nhưng bạn đúng rằng biểu diễn thời gian chạy là của Object. Bạn sẽ tìm thấy tuyên bố "gõ tĩnh để động" trong một số bài thuyết trình của MS.
Brian Rasmussen

3
@Thomas: và thông số ngôn ngữ cho biết "C # 4.0 giới thiệu một loại tĩnh mới gọi là động".
Brian Rasmussen

thực sự ... Nhưng tôi nghĩ thật khó hiểu khi coi nó là một loại, vì không có mối quan hệ thừa kế với các loại như DynamicObject hoặc ExpandoObject
Thomas Levesque

3
@NathanA Tôi với bạn ở đây. Tuy nhiên, đặc tả ngôn ngữ gọi nó là một loại, vì vậy đó là những gì tôi sẽ làm với.
Brian Rasmussen

0

Ví dụ trên DynamicObjectkhông cho biết sự khác biệt rõ ràng, vì về cơ bản, nó thực hiện chức năng đã được cung cấp bởi ExpandoObject.

Trong hai liên kết được đề cập dưới đây, rất rõ ràng rằng với sự trợ giúp của DynamicObject, có thể bảo tồn / thay đổi loại thực tế ( XElementtrong ví dụ được sử dụng trong các liên kết bên dưới) và kiểm soát tốt hơn các thuộc tính và phương thức.

https://bloss.msdn.microsoft.com/csharpfaq/2009/09/30/dynamic-in-c-4-0-int Giới thiệu-the-expandoobject /

https://bloss.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;    
        }    
    }    
}
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.