程序笔记   发布时间:2022-05-30  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了如何在保留装箱对象的前提下修改值大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

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

 

场景:

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),通过对象标识,就可以知道对象的往生来去了。另一种办法则是利用第二条知识,使用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,falsE);
            }
        }

        Idisposable Members
    }
}

大佬总结

以上是大佬教程为你收集整理的如何在保留装箱对象的前提下修改值全部内容,希望文章能够帮你解决如何在保留装箱对象的前提下修改值所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: