"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);
}
}
}
}