Класс System.Dynamic.ExpandoObject

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Класс ExpandoObject позволяет добавлять и удалять элементы его экземпляров во время выполнения, а также задавать и получать значения этих элементов. Этот класс поддерживает динамическую привязку, которая позволяет использовать стандартный синтаксис, например sampleObject.sampleMember вместо более сложного синтаксиса.sampleObject.GetAttribute("sampleMember")

Класс ExpandoObject реализует стандартный интерфейс IDynamicMetaObjectProviderсреды выполнения динамического ExpandoObject языка (DLR), который позволяет совместно использовать экземпляры класса между языками, поддерживающими модель взаимодействия DLR. Например, можно создать экземпляр ExpandoObject класса в C# и передать его в функцию IronPython. Дополнительные сведения см. в разделе "Обзор динамической языковой среды выполнения" и "Введение в expandoObject".

Класс ExpandoObject представляет собой реализацию концепции динамического объекта, которая позволяет получать, задавать и вызывать члены. Если вы хотите определить типы, имеющие собственную семантику динамической диспетчеризации, используйте DynamicObject класс. Если вы хотите определить, как динамические объекты участвуют в протоколе взаимодействия и управляют быстрой динамической кэшированием DLR, создайте собственную реализацию IDynamicMetaObjectProvider интерфейса.

Создание экземпляра

Чтобы включить позднюю привязку для экземпляра ExpandoObject класса, необходимо использовать dynamic ключевое слово. Дополнительные сведения см. в разделе Использование типа dynamic.

В Visual Basic динамические операции поддерживаются поздней привязкой. Дополнительные сведения см. в статье "Ранняя и поздняя привязка" (Visual Basic).

В следующем примере кода показано, как создать экземпляр ExpandoObject класса.

dynamic sampleObject = new ExpandoObject();
Dim sampleObject As Object = New ExpandoObject()

Добавление новых элементов

Свойства, методы и события можно добавить в экземпляры ExpandoObject класса.

В следующем примере кода показано, как добавить новое свойство в экземпляр ExpandoObject класса.

sampleObject.test = "Dynamic Property";
Console.WriteLine(sampleObject.test);
Console.WriteLine(sampleObject.test.GetType());
// This code example produces the following output:
// Dynamic Property
// System.String
sampleObject.Test = "Dynamic Property"
Console.WriteLine(sampleObject.test)
Console.WriteLine(sampleObject.test.GetType())
' This code example produces the following output:
' Dynamic Property
' System.String

Методы представляют лямбда-выражения, которые хранятся в качестве делегатов, которые могут вызываться при необходимости. В следующем примере кода показано, как добавить метод, который увеличивает значение динамического свойства.

sampleObject.number = 10;
sampleObject.Increment = (Action)(() => { sampleObject.number++; });

// Before calling the Increment method.
Console.WriteLine(sampleObject.number);

sampleObject.Increment();

// After calling the Increment method.
Console.WriteLine(sampleObject.number);
// This code example produces the following output:
// 10
// 11
sampleObject.Number = 10
sampleObject.Increment = Function() sampleObject.Number + 1
' Before calling the Increment method.
Console.WriteLine(sampleObject.number)

sampleObject.Increment.Invoke()

' After calling the Increment method.
Console.WriteLine(sampleObject.number)
' This code example produces the following output:
' 10
' 11

В следующем примере кода показано, как добавить событие в экземпляр ExpandoObject класса.

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

        // Create a new event and initialize it with null.
        sampleObject.sampleEvent = null;

        // Add an event handler.
        sampleObject.sampleEvent += new EventHandler(SampleHandler);

        // Raise an event for testing purposes.
        sampleObject.sampleEvent(sampleObject, new EventArgs());
   }

    // Event handler.
    static void SampleHandler(object sender, EventArgs e)
    {
        Console.WriteLine("SampleHandler for {0} event", sender);
    }
}
// This code example produces the following output:
// SampleHandler for System.Dynamic.ExpandoObject event.
Module Module1

Sub Main()
    Dim sampleObject As Object = New ExpandoObject()

    ' Create a new event and initialize it with null.
    sampleObject.sampleEvent = Nothing

    ' Add an event handler.
    Dim handler As EventHandler = AddressOf SampleHandler
    sampleObject.sampleEvent =
        [Delegate].Combine(sampleObject.sampleEvent, handler)

    ' Raise an event for testing purposes.
    sampleObject.sampleEvent.Invoke(sampleObject, New EventArgs())

End Sub

' Event handler.
Sub SampleHandler(ByVal sender As Object, ByVal e As EventArgs)
    Console.WriteLine("SampleHandler for {0} event", sender)
End Sub

' This code example produces the following output:
' SampleHandler for System.Dynamic.ExpandoObject event.

End Module

Передача в качестве параметра

Экземпляры ExpandoObject класса можно передавать в качестве параметров. Обратите внимание, что эти экземпляры рассматриваются как динамические объекты в C# и объекты с поздними привязками в Visual Basic. Это означает, что у вас нет IntelliSense для элементов объекта, и при вызове несуществующих элементов компилятора не получаются ошибки компилятора. При вызове элемента, который не существует, возникает исключение.

В следующем примере кода показано, как создать и использовать метод для печати имен и значений свойств.

class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // The following statement causes an exception
        // if you pass the employee object.
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.
Sub Main()
    Dim employee, manager As Object

    employee = New ExpandoObject()
    employee.Name = "John Smith"
    employee.Age = 33

    manager = New ExpandoObject()
    manager.Name = "Allison Brown"
    manager.Age = 42
    manager.TeamSize = 10

    WritePerson(manager)
    WritePerson(employee)
End Sub

Private Sub WritePerson(ByVal person As Object)

    Console.WriteLine("{0} is {1} years old.",
                      person.Name, person.Age)
    ' The following statement causes an exception
    ' if you pass the employee object.
    ' Console.WriteLine("Manages {0} people", person.TeamSize)

End Sub

Перечисление и удаление элементов

Класс ExpandoObject реализует интерфейс IDictionary<String, Object>. Это позволяет перечислять элементы, добавленные в экземпляр ExpandoObject класса во время выполнения. Это может быть полезно, если вы не знаете во время компиляции, какие члены экземпляра могут иметь.

В следующем примере кода показано, как привести экземпляр ExpandoObject класса в IDictionary<TKey,TValue> интерфейс и перечислить члены экземпляра.

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;

foreach (var property in (IDictionary<String, Object>)employee)
{
    Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33
Dim employee As Object = New ExpandoObject()
employee.Name = "John Smith"
employee.Age = 33
For Each member In CType(employee, IDictionary(Of String, Object))
    Console.WriteLine(member.Key & ": " & member.Value)
Next
' This code example produces the following output:
' Name: John Smith
' Age: 33

На языках, не имеющих синтаксиса для удаления элементов (таких как C# и Visual Basic), можно удалить элемент, неявно приведения экземпляра ExpandoObjectIDictionary<String, Object> в интерфейс, а затем удалить элемент в виде пары "ключ-значение". Это показано в следующем примере.

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
((IDictionary<String, Object>)employee).Remove("Name");
Dim employee As Object = New ExpandoObject()
employee.Name = "John Smith"
CType(employee, IDictionary(Of String, Object)).Remove("Name")

Получение уведомлений об изменениях свойств

Класс ExpandoObject реализует INotifyPropertyChanged интерфейс и может вызывать PropertyChanged событие при добавлении, удалении или изменении элемента. Это позволяет ExpandoObject интегрировать классы с привязкой данных Windows Presentation Foundation (WPF) и другими средами, требующими уведомления об изменениях содержимого объекта.

В следующем примере кода показано, как создать обработчик событий для PropertyChanged события.

// Add "using System.ComponentModel;" line
// to the beginning of the file.
class Program
{
    static void Test()
    {
        dynamic employee = new ExpandoObject();
        ((INotifyPropertyChanged)employee).PropertyChanged +=
            new PropertyChangedEventHandler(HandlePropertyChanges);
        employee.Name = "John Smith";
    }

    private static void HandlePropertyChanges(
        object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("{0} has changed.", e.PropertyName);
    }
}
' Add "Imports System.ComponentModel" line 
' to the beginning of the file.
Sub Main()
    Dim employee As Object = New ExpandoObject
    AddHandler CType(
        employee, INotifyPropertyChanged).PropertyChanged,
        AddressOf HandlePropertyChanges
    employee.Name = "John Smith"
End Sub

Private Sub HandlePropertyChanges(
       ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
    Console.WriteLine("{0} has changed.", e.PropertyName)
End Sub