C# 팁: ExpandoObject ν™œμš©

λ³Έ λ‚΄μš©μ€ CODE4IT의 C# Tip: Convert ExpandoObjects to IDictionary의 λ‚΄μš©μ„ λ³΄μ™„ν•œ κΈ€μž…λ‹ˆλ‹€.

ExpandoObject

μˆ™λ ¨λœ C# κ°œλ°œμžλΌλ„ ExpandoObjectλ₯Ό 처음 λ“€μ—ˆκ±°λ‚˜ μ•ŒκΈ°λŠ” ν•˜μ§€λ§Œ μ‚¬μš©ν•΄λ³΄μ§€ μ•Šμ•˜μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

ExpandoObject λŠ” dynamic ν˜•μ‹μ„ 톡해 λ™μ μœΌλ‘œ 속성을 μΆ”κ°€ν•˜κ³  μ‚¬μš©ν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

dynamic myObj = new ExpandoObject();
myObj.Name = "Davide";
myObj.Age = 30;

myObj μΈμŠ€ν„΄μŠ€λŠ” ExpandoObject μΈμŠ€ν„΄μŠ€μ΄μ§€λ§Œ dynamic ν˜•μ‹μ΄λ―€λ‘œ μ†μ„±μ˜ 쑴재 유무λ₯Ό 컴파일 μ‹œμ μ—μ„œ ν™•μΈν•˜μ§€ μ•ŠμœΌλ©° 속성을 μ‚¬μš©ν•˜λŠ” λŸ°νƒ€μž„ μ‹œμ μ—μ„œ μƒˆλ‘œμš΄ 속성λͺ…μœΌλ‘œ 값을 μ„€μ •ν•˜κ³  읽을 수 μžˆμŠ΅λ‹ˆλ‹€.

마치 파이썬과 같은 동적 μ–Έμ–΄ κ°™μ§€μš”?

λ™μ μœΌλ‘œ μΆ”κ°€λœ 속성은 μ–΄λ–»κ²Œ μ‘°νšŒν•  수 μžˆλ‚˜μš”?

ExpandoObject μΈμŠ€ν„΄μŠ€λŠ” λ™μ μœΌλ‘œ 속성이 계속 좔가될 수 있기 λ•Œλ¬Έμ— μ–΄λ–€ 속성이 μΆ”κ°€λ˜μ—ˆλŠ”μ§€ μ‘°νšŒκ°€ ν•„μš”ν•©λ‹ˆλ‹€.

μ΄λŠ” ExpandoObjectκ°€ κ΅¬ν˜„ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 κ°€λŠ₯ν•œλ°μš”

public sealed class ExpandoObject : ICollection<KeyValuePair<string, object?>>, IEnumerable<KeyValuePair<string, object?>>, IEnumerable, IDictionary<string, object?>, INotifyPropertyChanged, IDynamicMetaObjectProvider

KeyValuePair<string,object?>의 ICollection μΈν„°νŽ˜μ΄μŠ€ 및 IDictionary<string,object?> μΈν„°νŽ˜μ΄μŠ€κ°€ κ΅¬ν˜„λ˜μ–΄ 있기 λ•Œλ¬Έμ— λ‹€μŒ 처럼 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

using System.ComponentModel;
using System.Dynamic;

dynamic p = new ExpandoObject();

// Console.WriteLine(p.Name); // 아직 Name 속성이 μ—†μœΌλ―€λ‘œ μ˜ˆμ™Έ

p.Name = "David";
p.Age = 30;

IDictionary<string, object?> dic = p;

dic["Sex"] = "Male";

Console.WriteLine(p.Sex);

foreach (var kv in p)
{
    Console.WriteLine(kv);
}

| 좜λ ₯

Male
[Name, David]
[Age, 30]
[Sex, Male]

λ°˜μ‘ κ°œμ²΄λ‘œλ„ μ‚¬μš©

ExpandoObjectλŠ” λ˜ν•œ INotifyPropertyChanged μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ„œ λ‹€μŒμ²˜λŸΌ 속성이 변경될 λ•Œ λ°˜μ‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

using System.ComponentModel;
using System.Dynamic;

dynamic p = new ExpandoObject();

(p as INotifyPropertyChanged)!.PropertyChanged += (s, e) =>
{
    Console.WriteLine($"Changed Property : {e.PropertyName}");
};

// Console.WriteLine(p.Name); // 아직 Name 속성이 μ—†μœΌλ―€λ‘œ μ˜ˆμ™Έ

p.Name = "David";
p.Age = 30;

IDictionary<string, object?> dic = p;

dic["Sex"] = "Male";

Console.WriteLine(p.Sex);

foreach (var kv in p)
{
    Console.WriteLine(kv);
}

| 좜λ ₯

Changed Property : Name
Changed Property : Age
Changed Property : Sex
Male
[Name, David]
[Age, 30]
[Sex, Male]
μ’‹μ•„μš” 5

쒋은 λ‚΄μš©μ΄μ§€λ§Œ λ§Œμ•½ dorFuscator와 같은 λ‚œλ…ν™” μ†”λ£¨μ…˜μ„ μ΄μš©μ€‘μ΄λΌλ©΄ λŸ°νƒ€μž„ μ‹œ 였λ₯˜κ°€ λ°œμƒν•  κ°€λŠ₯성이 맀우 λ†’μ•„ ꢌμž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ’‹μ•„μš” 3

ExpandoObjectκ°€ INotifyPropertyChanged μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν–ˆκΈ° λ•Œλ¬Έμ— WPF λ“±μ—μ„œ 바인딩 개체둜 μ‚¬μš©ν•  수 μžˆκ² μ§€λ§Œ 아직 ν•΄λ³΄μ§€λŠ” μ•Šμ•„μ„œ κ°„λ‹¨νžˆ κ΅¬ν˜„ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. μ•„λž˜μ˜ μ†ŒμŠ€μ½”λ“œλ₯Ό 톡해 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

| MainWindow.xaml

...
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <Label Width="80">Name</Label>
                <TextBox Width="120" Text="{Binding UserInfo.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Width="80">Age</Label>
                <TextBox Width="80" Text="{Binding UserInfo.Age, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <Slider
                    Width="200"
                    Margin="8,0,0,0"
                    VerticalAlignment="Center"
                    IsSnapToTickEnabled="True"
                    Maximum="100"
                    Minimum="1"
                    TickFrequency="1"
                    Value="{Binding UserInfo.Age, Mode=TwoWay}" />
            </StackPanel>
            <Label>Output</Label>
            <StackPanel Orientation="Horizontal">
                <Label Width="80">Name</Label>
                <Label Content="{Binding UserInfo.Name, Mode=OneWay}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Width="80">Age</Label>
                <Label Content="{Binding UserInfo.Age, Mode=OneWay}" />
            </StackPanel>
        </StackPanel>
...

| MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        public dynamic UserInfo { get; } = new ExpandoObject();

        public MainWindow()
        {
            DataContext = this;

            InitializeComponent();

            //UserInfo.Name = "John";
        }
    }

image

μ†ŒμŠ€μ½”λ“œ

μ’‹μ•„μš” 6

μ•Œλ €μ£Όμ‹  방식이 μš”λŸ° μƒν™©μ—μ„œλŠ” κ½€ μœ μš©ν•˜κΈ΄ ν•©λ‹ˆλ‹€β€¦

λ¬Όλ‘  μš”λŸ° 상황은 자주 μ—†λ‹€λŠ” 게 함정…ㅋㅅㅋ


아… 써놓고 λ³΄λ‹ˆ 이건 ExpandoObject κ°€ μ•„λ‹ˆλΌ DynamicObject κ΅°μš”β€¦ 죄솑…;;;

μ’‹μ•„μš” 5

였우 멋진 κ²½ν—˜ 곡유 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€β€¦!

μ’‹μ•„μš” 2