Duck Typing in C#
"when I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck" -- James Whitcomb Riley
It's really easy to implement duck typing in C# 4.0 since it introduced dynamic into its toolsets. The following is just some scratch to help me familiar with the dynamic feature (plus dynamic proxy from castle project )
代码
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace DynamicDuckTypeing
{
class DynamicWrapper : DynamicObject
{
object _obj;
Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public DynamicWrapper(object obj)
{
_obj = obj;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return TrySetMember(binder.Name, value);
}
internal virtual bool TrySetMember(string name, object value)
{
var memberInfo = _obj.GetType().GetMember(name);
if (memberInfo.Count() == 0)
{
_dictionary[name] = value;
}
else
{
_obj.GetType().InvokeMember(name,
System.Reflection.BindingFlags.SetProperty,
null,
_obj,
new object[] { value });
}
return true;
}
internal virtual bool TryGetMember(string name, out object result)
{
var memberInfo = _obj.GetType().GetMember(name);
if (memberInfo.Count() == 0)
{
return _dictionary.TryGetValue(name, out result);
}
else
{
bool invokeSucceed = true;
try
{
result = _obj.GetType().InvokeMember(name,
System.Reflection.BindingFlags.GetProperty,
null,
_obj,
null);
}
catch
{
result = null;
invokeSucceed = false;
}
return invokeSucceed;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return TryGetMember(binder.Name,out result);
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
return TryInvokeMember(binder.Name, args, out result);
}
internal bool TryInvokeMember(string memberName, object[] args, out object result)
{
_obj.GetType().InvokeMember(memberName,
System.Reflection.BindingFlags.InvokeMethod,
null,
_obj, args);
result = null;
return true;
}
public override bool TryConvert (ConvertBinder binder, out object result)
{
result = Generator.GenerateProxy(binder.Type,this);
return true;
}
}
}
using System.Dynamic;
using System.Linq;
namespace DynamicDuckTypeing
{
class DynamicWrapper : DynamicObject
{
object _obj;
Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public DynamicWrapper(object obj)
{
_obj = obj;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return TrySetMember(binder.Name, value);
}
internal virtual bool TrySetMember(string name, object value)
{
var memberInfo = _obj.GetType().GetMember(name);
if (memberInfo.Count() == 0)
{
_dictionary[name] = value;
}
else
{
_obj.GetType().InvokeMember(name,
System.Reflection.BindingFlags.SetProperty,
null,
_obj,
new object[] { value });
}
return true;
}
internal virtual bool TryGetMember(string name, out object result)
{
var memberInfo = _obj.GetType().GetMember(name);
if (memberInfo.Count() == 0)
{
return _dictionary.TryGetValue(name, out result);
}
else
{
bool invokeSucceed = true;
try
{
result = _obj.GetType().InvokeMember(name,
System.Reflection.BindingFlags.GetProperty,
null,
_obj,
null);
}
catch
{
result = null;
invokeSucceed = false;
}
return invokeSucceed;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return TryGetMember(binder.Name,out result);
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
return TryInvokeMember(binder.Name, args, out result);
}
internal bool TryInvokeMember(string memberName, object[] args, out object result)
{
_obj.GetType().InvokeMember(memberName,
System.Reflection.BindingFlags.InvokeMethod,
null,
_obj, args);
result = null;
return true;
}
public override bool TryConvert (ConvertBinder binder, out object result)
{
result = Generator.GenerateProxy(binder.Type,this);
return true;
}
}
}
代码
using System;
using Castle.DynamicProxy;
using Castle.Core.Interceptor;
namespace DynamicDuckTypeing
{
class Generator
{
static readonly ProxyGenerator _generator = new ProxyGenerator();
internal static object GenerateProxy(Type type, DynamicWrapper dynamicWrapper)
{
return _generator.CreateInterfaceProxyWithoutTarget(type, new Interceptor(dynamicWrapper));
}
}
class Interceptor : IInterceptor
{
DynamicWrapper _wrapper;
public Interceptor(DynamicWrapper wrapper)
{
_wrapper = wrapper;
}
public void Intercept(IInvocation invocation)
{
object result;
//property access will be converted to get_XXX and set_XXX, use String.Substring(4) to get the real proertyName
if (invocation.Method.Name.StartsWith("get_"))
{
invocation.ReturnValue = _wrapper.TryGetMember(invocation.Method.Name.Substring(4), out result);
invocation.ReturnValue = result;
}
else if (invocation.Method.Name.StartsWith("set_"))
{
_wrapper.TrySetMember(invocation.Method.Name.Substring(4), invocation.Arguments[0]);
}
else
{
_wrapper.TryInvokeMember(invocation.Method.Name, invocation.Arguments, out result);
invocation.ReturnValue = result;
}
}
}
}
using Castle.DynamicProxy;
using Castle.Core.Interceptor;
namespace DynamicDuckTypeing
{
class Generator
{
static readonly ProxyGenerator _generator = new ProxyGenerator();
internal static object GenerateProxy(Type type, DynamicWrapper dynamicWrapper)
{
return _generator.CreateInterfaceProxyWithoutTarget(type, new Interceptor(dynamicWrapper));
}
}
class Interceptor : IInterceptor
{
DynamicWrapper _wrapper;
public Interceptor(DynamicWrapper wrapper)
{
_wrapper = wrapper;
}
public void Intercept(IInvocation invocation)
{
object result;
//property access will be converted to get_XXX and set_XXX, use String.Substring(4) to get the real proertyName
if (invocation.Method.Name.StartsWith("get_"))
{
invocation.ReturnValue = _wrapper.TryGetMember(invocation.Method.Name.Substring(4), out result);
invocation.ReturnValue = result;
}
else if (invocation.Method.Name.StartsWith("set_"))
{
_wrapper.TrySetMember(invocation.Method.Name.Substring(4), invocation.Arguments[0]);
}
else
{
_wrapper.TryInvokeMember(invocation.Method.Name, invocation.Arguments, out result);
invocation.ReturnValue = result;
}
}
}
}
代码
using System;
namespace DynamicDuckTypeing
{
public interface IQuack
{
void Quack();
}
class Duck
{
public void Quack()
{
Console.WriteLine("Duck Quack");
}
}
class Goose
{
public void Quack()
{
Console.WriteLine("Goose goose");
}
}
class Program
{
static void Main(string[] args)
{
dynamic dynamicDuck = new DynamicWrapper( new Duck());
dynamicDuck.Name = "mallard duck";
dynamicDuck.Age = 2;
Console.WriteLine("Name: {0}, Age: {1}",dynamicDuck.Name, dynamicDuck.Age);
dynamicDuck.Quack();
Console.WriteLine();
IQuack q = dynamicDuck;
q.Quack();
dynamic dynamicGoose = new DynamicWrapper(new Goose());
q = dynamicGoose;
q.Quack();
q = (dynamic)new DynamicWrapper(new object());
try
{
q.Quack();
Console.WriteLine("can't be there");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
namespace DynamicDuckTypeing
{
public interface IQuack
{
void Quack();
}
class Duck
{
public void Quack()
{
Console.WriteLine("Duck Quack");
}
}
class Goose
{
public void Quack()
{
Console.WriteLine("Goose goose");
}
}
class Program
{
static void Main(string[] args)
{
dynamic dynamicDuck = new DynamicWrapper( new Duck());
dynamicDuck.Name = "mallard duck";
dynamicDuck.Age = 2;
Console.WriteLine("Name: {0}, Age: {1}",dynamicDuck.Name, dynamicDuck.Age);
dynamicDuck.Quack();
Console.WriteLine();
IQuack q = dynamicDuck;
q.Quack();
dynamic dynamicGoose = new DynamicWrapper(new Goose());
q = dynamicGoose;
q.Quack();
q = (dynamic)new DynamicWrapper(new object());
try
{
q.Quack();
Console.WriteLine("can't be there");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}