방프리
24.04.29 Chapter5. 동적 프로그래밍 (Item 45) 본문
Item 45 : 데이터 주도 동적 타입에는 DynamicObject나 IDynamicMetaObjectProvider를 사용하라
동적 프로그래밍의 큰 장점 하나는 런타임에 public 인터페이스가 바뀌는 타입을 만들 수 있다는 것이다. C#에서는 dynamic과 System.Dynamic.DynamicObject 베이스 클래스 그리고 System.Dynamic.IDynamicMetaObjectProvider 인터페이스를 통해 동적 능력을 갖춘 고유의 타입을 만들 수 있다.
동적 능력을 가지는 타입을 만드는 다음과 같다.
1. System.Dynamic.DynamicObject 상속
- 가장 구현이 쉬운 방법 이 타입은 IDynamicMetaObjectProvider 인터페이스를 구현한 중첩 private 클래스를 가지고 있고,
이 중첩 private 클래스는 표현식을 파싱하고 결과를 DynamicObject 클래스의 가상 메서드에 전달하는 등의 많은 일을 수행한다.
dynamic dynamicProperties = new DynamicPropertyBag();
try
{
Console.WriteLine(dynamicProperties.Marker);
}
catch (MIcrosoft.CSharp.RuntimeBinder.RuntimeBinderException)
{
Console.WriteLine("There are no properties");
}
dynamicProperties.Date = DateTime.Now;
dynamicProperties.Name = "Bill Wagner";
dynamicProperties.Title = "Effective C#";
dynamicProperties.Content = "Building a dynamic dictionary";
//DynamicObject의 TrySetMember와 TryGetMember메서드를 재정의한 DynamicPropertyBag
class DynamicPropertyBag : DynamicObject
{
private Dictionary<string, object> storage =
new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (storage.ContainsKey(binder.Name))
{
result = storage[binder.Name];
return true;
}
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
string key = binder.Name;
if (storage.ContainsKey(key))
storage[key] = value;
else
storage.Add(key, value);
return true;
}
public override string ToString()
{
StringWriter message = new StringWriter();
foreach (var item in storage)
message.WritenLine($"{item.Key}:\t{item.Value}");
return message.ToString();
}
}
2. 다른 베이스 클래스를 상속해야 해서 DynamicObject를 상속할 수 없다면 IDynamicMetaObjectProvider를 구현해서 동적 딕셔너리를 만들면 된다.
class DynamicDictionary2 : IDynamicMetaObjectProvider
{
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DynamicDictionaryMetaObject(parameter, this);
}
private Dictionary<string, object> storage = new Dictionary<string, object>();
public object SetDictionaryEntry(string key, object value)
{
if (storage.ContainsKey(key))
storage[key] = value;
else
storage.Add(key, value);
return value;
}
public object GetDictionaryEntry(string key)
{
object result = null;
if (storage.ContainsKey(key))
{
result = storage[key];
}
return result;
}
public override string ToString()
{
StringWriter message = new StringWriter();
foreach (var item in storage)
message.WriteLine($"{item.Key}:\t{item.Value}");
return message.ToString();
}
public override DynamicMetaObject BindSetMember(
SetMemberBinder binder,
DynamicMetaObject value)
{
string methodName = "SetDictionaryEntry";
BindingRestrictions restrictions =
BindingRestrictions.GetTypeRestriction(Expression, LimitType);
Expression[] args = new Expression[2];
args[0] = Expression.Constant(binder.Name);
args[1] = Expression.COnvert(value.Expression, typeof(object));
Expression self = Expression.Convert(Expression, LimitType);
Expression methodCall = Expression.Call(self,
typeof(DynamicDctionary2).GetMethod(methodName), args);
DynamicMetaObject setDictionaryEntry =
new DynamicMetaObject(methodCall, restrictions);
return setDictionaryEntry;
}
public override DynmaicMetaObject BindGetMember(GetMemberBinder binder)
{
string methodName = "GetDictionaryEntry";
Expression[] parameters = new Expression[]
{
Expression.Contant(binder.Name)
};
DynamicMetaObject getDictionaryEntry =
new DynamicMetaObject(
Expression.Call(
Expression.Convert(Expression, LimitType),
typeof(DynamicDictionary2).GetMethod(methodName), parameters),
BindingRestrictions.GetTypeRestriction(Expression, LimitType));
return getDictionaryEntry;
}
}
동적 객체를 호출할 때마다 새로운 DynamicMetaObject를 생성하고 이 객체의 Bind 멤버 중 하나를 호출한다. 따라서 이 메서드들은 효율과 성능을 고려해서 작성해야 한다.
동적 타입을 만들어야 한다면 가장 먼저 System.Dynamic.DynamicObject를, 다른 베이스 클래스를 사용해야 해서 어쩔 수 없다면 IDynamicMetaObjectProvider를 구현하자. 단 구현이 쉽지 않고 동적 타입은 성능을 다소 떨어뜨리며 직접 구현 시 그 정도가 더 심해진다.
'C# > More Effective C#' 카테고리의 다른 글
24.05.04 Chapter5. 동적 프로그래밍 (Item 43) (0) | 2024.05.04 |
---|---|
24.04.30 Chapter5. 동적 프로그래밍 (Item 46) (0) | 2024.04.30 |
24.04.28 Chapter5. 동적 프로그래밍 (Item 44) (0) | 2024.04.28 |
24.04.24 Chapter4. 병렬 처리 (Item 42) (0) | 2024.04.24 |
24.04.23 Chapter4. 병렬 처리 (Item 41) (0) | 2024.04.23 |