如何在保留装箱对象的前提下修改值

前两天给别人解答装箱问题时,有人问如何在保留装箱对象的前提下修改值?回头想了想,趁今天一会闲暇实现了出来,牵出来溜溜。

场景:
object  obj  =   100 ;
Console.WriteLine(
" original object value:  "   +  obj.ToString()); // when debug, make obj's ID: 1#
// TODO: modify obj value here (to 1000, for example), but preserve obj object
Console.WriteLine( " modified object value:  "   +  obj.ToString()); // make sure obj's ID: 1#

分析:
显然这里直接obj = 1000是不行的,那样之后得到的是对1000装箱的对象,而不是对100的装箱对象了,那么如何修改呢?

首先,这里列出本文涉及的一些.NET和CLR的准备知识——装箱的对象的分配和存储、对象的托管内存地址获取、对象唯一性确定、托管内存数据读写。如果你不是很熟悉,没关系,经过本篇的实践,加上MSDN的解释,你很快就可以理解。

1、对象的分配和存储。这里设计的仅仅是部分,细节可以参考CLR via。对象分配在托管堆上,由几个部分组成,第一部分是存储的是对象类型的TypeHandle,其后内容随类型不同而不同;对于装箱对象,其后紧跟的内存存储的是装箱的值(就是我们要找到然后去修改的东东了)。
2、对象的托管内存地址获取。通过System.Runtime.InteropServices.GCHandle类和其上的静态方法获取。
3、对象唯一性确定。这个方法有两种,第一种,需要依赖VS IDE Debug环境,在IDE的debug下,可以对任何对象设置对象标识(object ID,具体可以参考我的另一篇,链接在这里:http://www.cnblogs.com/winkingzhang/archive/2008/01/29/1057534.html),通过对象标识,就可以知道对象的往生来去了。另一种办法则是利用第二条知识,使用GCHandle的IsAllocated来判断。
4、通过上面得到了托管地址,如何修改托管地址处保存的内容呢?使用System.Runtime.InteropServices.Marshal.StructureToPtr或者System.Runtime.InteropServices.Marshal.WriteXXX系列方法即可。

基于以上内容,我们可以可以做到在保留装箱对象的前提下修改值了,显然首先需要的是装箱对象的引用,然后调用System.Runtime.InteropServices.GCHandle.Aloc(object)得到托管地址,该托管地址指向的内容就是装箱的对象;由于装箱对象的第一部分是TypeHandle,所以需要将指针向后偏移IntPtr.Size得到数据存储地址,然后通过Marshal.StructureToPtr写入新的内容即可。代码片段如下(完整部分参考最后附录):

if  ( ! _memData.IsAllocated)
{
    _memData 
= GCHandle.Alloc(_boxedObject);
}

IntPtr pMemData 
=  GCHandle.ToIntPtr(_memData);
IntPtr pBox 
=   new  IntPtr((Marshal.ReadIntPtr(pMemData).ToInt64()  +  IntPtr.Size));
Marshal.StructureToPtr(value, pBox, 
false );

结果:

讨论:
显然这里写入数据时候是需要很小心的,因为如果装箱的数据占用内存小,而写入的数据比它大的话,就会触发AccessViolationException,甚至导致溢出,形成安全漏洞。

额外话题:
如果传入的就是一个引用类型的实例,会是什么结果呢? 
还等什么呢,赶快自己动手试试喽。

附录,完整的测试代码:
using  System;
using  System.Runtime.InteropServices;

namespace  BoxedObjectWriter
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
object test = 100;
            Console.WriteLine(
"original value=" + test.ToString() + ", hash=" + test.GetHashCode());
            BoxedObject b 
= new BoxedObject(test);
            b.Value 
= 1000;
            Console.WriteLine(
"after edit value=" + test.ToString() + ", hash=" + test.GetHashCode());

            Console.ReadLine();
        }

    }


    
public class BoxedObject : IDisposable
    
{
        
private object _boxedObject;
        
private GCHandle _memData;

        
public BoxedObject(object boxObject)
        
{
            
if (boxObject == null)
            
{
                
throw new ArgumentNullException();
            }

            _boxedObject 
= boxObject;            
        }


        
~BoxedObject()
        
{
            (
this as IDisposable).Dispose();
        }


        
public object Value
        
{
            
get return _boxedObject; }
            
set
            
{
                
if (value == null)
                
{
                    
throw new ArgumentNullException();
                }

                
if (value.GetType() != _boxedObject.GetType())
                
{
                    
throw new NotSupportedException(string.Format("Can not set [{0}] value to [{1}] object",
                        value.GetType().Name,
                        _boxedObject.GetType().Name));
                }


                
if (!_memData.IsAllocated)
                
{
                    _memData 
= GCHandle.Alloc(_boxedObject);
                }

                IntPtr pMemData 
= GCHandle.ToIntPtr(_memData);
                IntPtr pBox 
= new IntPtr((Marshal.ReadIntPtr(pMemData).ToInt64() + IntPtr.Size));
                Marshal.StructureToPtr(value, pBox, 
false);
            }

        }


        
IDisposable Members#region IDisposable Members
        
void IDisposable.Dispose()
        
{
            
if (_memData.IsAllocated)
                _memData.Free();
        }

        
#endregion

    }

}

转载于:https://www.cnblogs.com/winkingzhang/archive/2008/02/27/1083732.html

微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码
微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值