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
<stringobject> _dictionary = new Dictionary<stringobject>();

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

        }
    }
}

 

 

 

                                                       

posted @ 2010-01-05 17:00 芭蕉 阅读(...) 评论(...) 编辑 收藏