zhuweisky

君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。

ESFramework,基于.NET的通信框架。DataRabbit,轻量的数据访问框架。sky.zhuwei@163.com
posts - 191, comments - 1276, trackbacks - 94, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

当泛型的参数类型是动态的...

Posted on 2007-04-26 17:58 zhuweisky 阅读(3507) 评论(17)  编辑 收藏 所属分类: C#专栏
   在使用泛型技术时,我们经常使用静态泛型参数,即泛型的参数类型在编译时就已经确定,比如:
   IList<int> list = new List<int>();
   集合IList的泛型参数是int类型,这在编译期就可以知道,于是,我们可以这样调用IList<>的Add方法:
   list.Add(6) ;

   如果泛型的参数类型是动态的了,也就是说,只有当程序运行起来后,才知道泛型参数的具体类型,其可能是int,也可能是string或其它。那该如何?使用反射,大家都知道这个答案,是的,使用反射可以解决问题,但是,反射就会有两个与身俱来的缺陷:一是丧失了强类型的好处,二是效率不高。

   还有一种更优雅的解决方案,那就是使用动态代理。针对上述的例子,我们首先定义一个非泛型的接口:
     /// <summary>
    
/// ISimpleList 用于创建动态代理,将运行时的IList<>接口转换为静态的ISimpleList接口
    
/// </summary>
    public interface ISimpleList
    {
        
void Add(object element);
    }
   然后,调用ESBasic.Emit.DynamicTypeEmitter类的static CreateDynamicProxy方法,创建动态代理实例,该动态代理实现了ISimpleList接口,接着便可以调用ISimpleList的Add方法来添加元素到集合中。
        public static void CallAddDemo(Type listElementType ,object addedElement)
        {            
            Type closedGenericListType 
= typeof(List<>).MakeGenericType(listElementType);
            
object list = Activator.CreateInstance(closedGenericListType);
            ISimpleList simpleList 
= (ISimpleList)DynamicTypeEmitter.CreateDynamicProxy<ISimpleList>(list);
           
            simpleList.Add(addedElement);
        }
   动态代理解决方案的效率因素:
(1)同一个动态代理类型只会生成一次,ESBasic会缓存生成的代理类型,以后便不会重复生成。
(2)动态代理的方法的调用将直接转发(如 前面的ISimpleList.Add方法会直接转发给IList<>.Add),不会有反射的效率损失。
(3)如果是值类型元素,那么可能会有box和unbox的性能损失;对于引用类型,则不存在。

   下载ESBasic.dll

Feedback

#1楼    回复  引用  查看    

2007-04-26 19:13 by 笑望人生      
不用这么麻烦,范型里的T可以是动态的,条件就是继承
 
public sealed class MyList<T> : List<T> where T : class, IEnumable
{
   
// 实现
}

以上就是自己的范型集合类,它继承了List<> 类,并且后面的where 关键字表示T必须是一个类并且继承了IEnumable接口。

#2楼    回复  引用    

2007-04-26 19:22 by Digital Castle [未注册用户]
的确,使用T就可以了

#3楼    回复  引用  查看    

2007-04-26 21:03 by 代码乱了      
不是很明白动态代理的实现。。。

#4楼    回复  引用    

2007-04-26 23:09 by hyifeng [未注册用户]
@笑望人生
你误会了,
楼主说 “只有当程序运行起来后,才知道泛型参数的具体类型”


另外,我很奇怪楼主为什么要把泛型集合这样用,
为什么不直接用List?

#5楼    回复  引用  查看    

2007-04-27 05:38 by Jeffrey Zhao      
直接用存放object的List有什么缺点吗?为什么要这么做呢?

#6楼    回复  引用  查看    

2007-04-27 09:23 by 装配脑袋      
既然代理的接口需要手工声明,那么手工定义代理又何妨呢?
class Program
{
    
static void Main(string[] args)
    
{
        ISimpleList sl 
= GetSimpleList(typeof(int));
        sl.Add(
12);
    }


    
static ISimpleList GetSimpleList(Type elementType)
    
{
        Type listType 
= typeof(List<>).MakeGenericType(elementType);
        
object listInstance = Activator.CreateInstance(listType);

        Type helperType 
= typeof(SimpleListHelper<>).MakeGenericType(elementType);

        
return (ISimpleList)Activator.CreateInstance(helperType, listInstance);
    }

}


interface ISimpleList
{
    
void Add(object item);
}


class SimpleListHelper<T> : ISimpleList
{
    
private IList<T> m_internalList;

    
public SimpleListHelper(IList<T> list)
    
{
        m_internalList 
= list;
    }


    
ISimpleList Members
}

#7楼    回复  引用  查看    

2007-04-27 09:25 by lyb      
foreach (PropertyInfo pi in t.GetProperties(bf))
{
    //把控件属性Text的值赋给entity    
pi.SetValue(entity, s,null);
}

我想把传过来的字符串 赋给entity 的属性 pi,

但是entity的属性类型不一样,所以我想把s 动态转型为 pi.PropertyType,
但是我这样写(pi.PropertyType)s ,编译不了的.

这个问题也可以这样做?

#8楼    回复  引用  查看    

2007-04-27 09:35 by 目标年薪三千万      
感觉就是用工厂模式生成了个泛型类而已,是一种思路.

#9楼    回复  引用    

2007-04-27 09:58 by 笑傲江湖 [未注册用户]
@装配脑袋 :
动态代理 的目标就是为了不再需要手工定义代理了

#10楼    回复  引用  查看    

2007-04-27 10:16 by 装配脑袋      
8好意思,能不能压缩成zip格式,RAR的解不开呀。

#11楼    回复  引用    

2007-04-27 10:36 by A.Z [未注册用户]
DynamicProxy = T

#12楼    回复  引用    

2007-04-27 11:10 by A.Z [未注册用户]
DynamicProxy的年代久远了...他的主旨通过代理对象的注入实现后期的方法接口绑定。
一般的用T可以描述常规的模型,T是基于编译时的。

#13楼    回复  引用    

2007-08-13 22:39 by Hunts [未注册用户]
Mark() 遇到这个问题了

#14楼    回复  引用  查看    

2007-10-09 14:43 by MS的明天      
很实际的问题

#15楼    回复  引用    

2008-06-27 08:33 by oriesmap [未注册用户]
支持楼主。
或许仅仅是楼主举的那个List<>的例子才让大家觉得使一个对象能够具备在运行时动态添加接口的能力是没有必要的。但是想一想,那些装饰模式所需要解决的问题吧。但是装饰模式必须要继承于一个接口是让人郁闷的。动态代理则有着更优雅的解决能力。
看过楼主的源码,基本思路来源于www.codeproject.com中的那个动态代理的例子,但是还是赞一个。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: