百科问答小站 logo
百科问答小站 font logo



如何用c#实现一个没有装箱的通用容器? 第1页

  

user avatar   sylphea 网友的相关建议: 
      

不邀自来,起码两种办法,能混合用,更多可以再想。

一、人家 Stack Overflow 的答案很好了,你还想干嘛?用 StructLayout 不香吗?

二、自定义 implicit cast,共享一个通用值,所有的值都能换成它(implicit),它也能换成所有要支持的值(explicit)。装箱成本其实不高,顶多顶多 40 次整数加法而已,算上垃圾回收,最多最多等同 100-200 次加法,因此如果你的转换代码太长,结果会不划算。值倘使超过 32 字节,大于 CPU 缓存线,一般不如装箱,毕竟缓存掉页一次也就可能 100-200 次加法了。微软官方 2002 年建议,凡大于 16 字节的数据,不适合用值,而应该用类,即强迫装箱,不过现代 CPU 都 64 位了,缓存线也至少 32 字节,我个人接受以 32 字节为上限。

想要单凭技术升迁加薪,专攻性能是最快的,而性能优化最显著的,是 I/O 优化,通常可以短时间内提升十到百倍速度。听其他答主的话,别再问一堆有的没的无聊问题了。


你大概没懂我的意思。我的意思是,用 struct CommonMsg : IMsg,然后定义 WalkMsg、RunMsg、AttackMsg 跟 CommonMsg 的双向 implicit/explicit cast,很方便。请注意把双向 implicit/explicit cast 的定义摆在 WalkMsg、RunMsg、AttackMsg,这样 CommonMsg 在最底层的 assembly,而其它值可以放其它 assemblies。为了 CommonMsg 最通用化,它可以排两个或四个长整数,挤下任何不过大的值。配合 StructLayout,你可以优化 implicit/explicit cast 的代码,两三步完成转换。


抱歉,昨晚半夜两点写的,昏昏沉沉,犯了个严重错误。拿出容器时,你需要知道它哪种值,怎办?很简单,CommonMsg 存个 RuntimeTypeHandle 就行了,explicit cast 时可以报错。微软 C# 看到 value.GetType().TypeHandle 会跳过 GetType() 而不装箱,RuntimeTypeHandle 只是个长整数(64 位 CPU),尚可接受。你如果觉得长整数太大,也能用小一点的整数,自己加个 _typeCount++ 去算。

那你要不想写 switch 或一大串 if-else 呢?也很简单,用 Dictionary<RuntimeTypeHandle, Action> 就可以多态了。字典有很多好处,你能动态维护多态,也能 double dispatch、multiple dispatch、dynamic dispatch 或任何 dispatch,超级非常灵活。况且,Action 是 Delegate,因此能 JIT inline,会接受 MethodImplOptions 的暗示,包括其中你喜欢的 AggressiveInlining 跟 AggressiveOptimization,比虚函数快!微软曾经承诺,将考虑 inline 虚函数,超越 Java 在这方面的成就,但我认为大可不必相信渣男。起码在 .Net 8 之前,微软不太可能 inline 虚函数,所以你想 inline 多态函数,那只能靠 Dictionary<RuntimeTypeHandle, Action> 了。

微软在承诺方面是个渣男,但在实际表现方面却不,Java 至今无法支持类似 Unity 的游戏引擎,说明 .Net 是至少比 Java 快四五倍的(if not 十倍)。目前除了虚函数 inlining、类本身的垃圾回收,.Net 在所有方面性能是超越 Java 的。缺乏类本身的垃圾回收是 .Net 最大败笔,因为 Java 可以支持上亿个类,而 .Net 由于无法回收类的 metadata,会挤爆内存,除非你想经常上下载 AppDomain,慢死程序。

所以,想要快,选 .Net;而想要大,选 Java。两者各有千秋。


user avatar   dnfl 网友的相关建议: 
      

严格来说,不能写出一个不装箱的通用容器,写出别的通用容器那也跟object差不多,毕竟object就是为了通用而生的

但是,在你这个应用场景,还是能有尽量不产生额外GC压力的方法的,首先各种msg作为结构体都有其相同不变的字段,这些字段完全可作为原始msg的字段,然后用一个索引content去运行时组装成新的msg,而不是编译时扩展,如下

       struct MSG {     All allHaveFields;    //用结构包装     type of index(integer or pointer or reference) content; } type index {     enum_HowToUseThisMSG filed1;     AdditinalMSG TheAdditinalMSG {get;}; } class or struct with interface WALKMSG   //需要被装箱的部分 {      private ownFields;      public void Main(All all){};   //所有扩充MSG都需要实现 } static class HandleMSG {     type Handle(MSG msg)     {         swtich (msg.content.field1)         {              case walk: msg.content.TheAdditinalMSG.Main(msg.all);break;         }     } }      

这个只是个大概的数据行为模型,但是核心思想就是用组装代替扩展和利用索引进行再映射

另外,完全不装箱,也是可以的,那就是采用指针利用byte数组手动使用指针存取,写成非托管类型,但是这种内存模型就更难分析,在实现上,难以扩展,必须手动维护前后一致。


user avatar   ksgfk-59 网友的相关建议: 
      

每个结构体布局都可能不一样啊...一个24bit一个32bit存不到一起去...非要一个容器统一处理的似乎只能开个byte数组,手动记录每个实例的元信息,比如偏移量,大小,类型,存取的时候强转(写着写着变成C了




  

相关话题

  如何用c#实现一个没有装箱的通用容器? 
  如何评价 ASP.NET vNext? 
  2019 年了,C# 发展得怎么样了? 
  .Net的垃圾回收机制是定时执行还是事件触发? 
  有没有什么工具可以帮助理解源代码的? 
  对象和实例的在不同编程语言中的有什么区别? 
  有哪些好的C#开源项目推荐? 
  最近好多人聊低代码,难道大家没听过C#开发windows form么? 
  用 Lazarus 做 GUI 程序合适吗? 
  为什么不能能向方法同时传入dynamic 类型,和lambda类型的参数? 

前一个讨论
假如你是一个男生,有一天你和一名女生走在校道上,那名女生污蔑你尾随她,说你对她进行了骚扰,如何辩护?
下一个讨论
C# 异步中Task.Wait的坑? C# Task.Wait为什么不等待就返回?





© 2024-12-26 - tinynew.org. All Rights Reserved.
© 2024-12-26 - tinynew.org. 保留所有权利