[C# 4.0] 2. Dynamic (2)

来源:互联网 发布:卖证券的网络销售 编辑:程序博客网 时间:2024/05/16 02:19

.NET 4.0 通过嵌入 DLR Runtime 来实现对动态编程的支持。DLR Runtime 包含 Expression Trees、CallSite Caching、Dynamic Object Interoperability 几个子系统。某些实现部分,我们在前一章分析 dynamic 关键字时已有所接触。

动态编程行为主要两个部分:

  • 动态访问对象 : 这也就是 dynamic 所要做的主要工作,我们可以在运行时 "动态" 访问对象成员,而不是编译期。
  • 创建动态对象 : 我们可以在运行期定义对象的行为,包括增删对象成员等。想必大家对动态语言的 OpenClass 已经神往已久了。

我们试着创建一些 .NET 4.0 动态调用的例子。和前一章 "动态" 调用 "静态对象成员" 不同,下面这些例子着重于 "动态对象"。

class MyClass : DynamicObject
{
private Dictionary<string, object> values = new Dictionary<string, object>();

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (!values.ContainsKey(binder.Name)) return false;

result = values[binder.Name];
return true;
}

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

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
switch (binder.Name)
{
case "Test":
var sb = new StringBuilder();
for (int i = 0; i < binder.CallInfo.ArgumentCount; i++)
{
sb.AppendFormat("{0}={1}/r/n", binder.CallInfo.ArgumentNames[i], args[i]);
}

result = sb.ToString();
return true;
default:
result = null;
return false;
}
}
}

class Program
{
static void Main(string[] args)
{
dynamic o = new MyClass();
o.X = 123;
o.Y = "Hello, World!";
var s = o.Test(A: 123, Y: "abc");

Console.WriteLine(o.X);
Console.WriteLine(o.Y);
Console.WriteLine(s);
}
}


输出:

123
Hello, World!
A=123
Y=abc


由于 DynamicObject 默认没有实现操作,因此在上面例子中我们得自己折腾。

public class DynamicObject : IDynamicMetaObjectProvider
{
public virtual bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
return false;
}

... ...
}


当然,如果嫌麻烦,可以考虑用下面这种方式。

dynamic o = new ExpandoObject();
o.Test = new Func<int, string, string>((a, y) => String.Format("A={0}/r/nY={1}", a, y));

o.X = 123;
o.Y = "Hello, World!";
var s = o.Test(123, "abc");

Console.WriteLine(o.X);
Console.WriteLine(o.Y);
Console.WriteLine(s);


和 JavaScript、Python 之类的动态语言很像吧。我们还可以对其成员进行增删和遍历操作,还记得 Python 中的 dir() 吧。

dynamic o = new ExpandoObject();
o.Test = new Func<int, string, string>((a, y) => String.Format("A={0}/r/nY={1}", a, y));
o.X = 123;
o.Y = "Hello, World!";

// 遍历成员
var dict = o as IDictionary<string, object>;
Console.WriteLine(String.Join(", ", dict.Keys));

// 移除成员
dict.Remove("Y");
try
{
Console.WriteLine(o.Y);
}
catch (RuntimeBinderException ex)
{
Console.WriteLine(ex.Message);
}


DynamicObject 和 ExpandoObject 都实现了 IDynamicMetaObjectProvider 接口,这是 DLR 进行动态操作所必须的。

public class DynamicObject : IDynamicMetaObjectProvider
{
// Methods
protected DynamicObject();
public virtual IEnumerable<string> GetDynamicMemberNames();
public virtual DynamicMetaObject GetMetaObject(Expression parameter);
public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result);
public virtual bool TryConvert(ConvertBinder binder, out object result);
public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);
public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
public virtual bool TryDeleteMember(DeleteMemberBinder binder);
public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result);
public virtual bool TryGetMember(GetMemberBinder binder, out object result);
public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result);
public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);
public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value);
public virtual bool TrySetMember(SetMemberBinder binder, object value);
public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
}

public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary<string, object>,
ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>,
IEnumerable, INotifyPropertyChanged
{
// Events
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;

// Methods
public ExpandoObject();
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item);
void ICollection<KeyValuePair<string, object>>.Clear();
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item);
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex);
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item);

void IDictionary<string, object>.Add(string key, object value);
bool IDictionary<string, object>.ContainsKey(string key);
bool IDictionary<string, object>.Remove(string key);
bool IDictionary<string, object>.TryGetValue(string key, out object value);

IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator();

DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter);

// Properties
int ICollection<KeyValuePair<string, object>>.Count { get; }
bool ICollection<KeyValuePair<string, object>>.IsReadOnly { get; }
object IDictionary<string, object>.this[string key] { get; set; }

ICollection<string> IDictionary<string, object>.Keys { get; }
ICollection<object> IDictionary<string, object>.Values { get; }
}


DynamicObject 集中实现成员调用,可以更灵活地调度。而 ExpandoObject 则更轻便,更接近于动态语言编程习惯。

原创粉丝点击