如何在泛型类中实现非泛型ISetter接口的泛型方法并避免值装箱?
如何实现无装箱的ISetter接口赋值
当然有办法避免装箱!核心思路是利用非托管类型转换绕开CLR的装箱机制,System.Runtime.CompilerServices.Unsafe类正好提供了这样的能力——它能直接在内存层面转换类型,不需要把值类型包装成object。
问题根源
你原来的代码里Value = (T)(object)value;之所以会装箱,是因为当T是值类型时,把T1先转成object会触发装箱操作,再转成T又要拆箱。而我们需要的是直接把T1的内存内容当成T来用,前提是你已经保证调用Set<T1>时T1和T是完全相同的类型。
修改后的无装箱实现代码
using System.Runtime.CompilerServices; public interface ISetter { void Set<T>(T value); } public class Prop<T> : ISetter { public T Value; // 确保调用时T1 == T,否则会引发内存错误 public void Set<T1>(T1 value) { if (typeof(T1) != typeof(T)) throw new ArgumentException(); // 直接在内存层面转换类型,无装箱/拆箱 Value = Unsafe.As<T1, T>(ref value); } }
关键说明
Unsafe.As<TFrom, TTo>(ref TFrom source)方法会直接把source的引用转换成TTo类型的引用,本质是内存地址的直接映射,完全不会涉及装箱操作——不管T是值类型还是引用类型都适用。- 必须严格保留
typeof(T1) != typeof(T)的检查:因为Unsafe类的操作跳过了CLR的类型校验,一旦类型不匹配,会直接导致内存访问错误,这个前置检查是安全保障的关键。 - 需要添加对
System.Runtime.CompilerServices命名空间的引用,这个类在.NET Core 2.0+、.NET Framework 4.6.1+以及.NET 5+中都可用。
备选方案(基于强类型委托)
如果你不想使用Unsafe类,还可以提前创建强类型委托来实现无后续装箱的赋值:
public class Prop<T> : ISetter { public T Value; private Action<T> _setter; public Prop() { _setter = v => Value = v; } public void Set<T1>(T1 value) { if (typeof(T1) != typeof(T)) throw new ArgumentException(); // 这里的转换安全,因为T1和T类型完全一致 ((Action<T1>)(object)_setter)(value); } }
这个方案里,首次创建委托时会有一次装箱,但后续的赋值操作不会再产生装箱开销——不过相比Unsafe方案,它还是多了一次初始装箱,性能上略逊一筹。
内容的提问来源于stack exchange,提问作者LionAM
相关产品推荐
相关产品推荐

