C#中Socket.Receive无需ref/out即可修改数组参数的原理问询
Socket.Receive方法可修改传入字节数组的底层原理
- 首先明确C#的传参规则:
byte[]属于引用类型,默认按值传递参数时,传递的是指向堆中数组实例的引用的副本,而非数组本身的副本。副本与外部的buff变量指向堆中完全相同的内存块,方法内部修改该内存块存储的数据,外部访问同一块内存自然能看到变更。 ref/out关键字的作用是修改引用本身的指向:只有当你需要在方法内部让传入的变量指向一个全新的对象实例时,才需要使用这两个关键字。Receive方法的逻辑是往你预先申请好的数组内存里写入接收到的数据,从来不会替换你传入的数组对象,完全不需要修改引用的指向,因此不需要加ref/out。- 底层实现逻辑:
Socket.Receive最终会调用操作系统原生Socket API,底层会直接将网卡缓冲区收到的数据拷贝到你传入的数组对应的内存地址上,本质是修改已有内存空间的内容,不会变更数组对象的引用地址。
你可以通过一段简单的测试代码验证该逻辑:
void ModifyArrayContent(byte[] arr) { // 修改引用指向的对象的内容,不需要ref arr[0] = 0xAA; } byte[] testBuff = new byte[1]; ModifyArrayContent(testBuff); Console.WriteLine(testBuff[0]); // 输出170,即0xAA,证明外部数组已被修改
如果尝试在方法内部替换数组对象,不加ref的话外部不会生效:
void ReplaceArray(byte[] arr) { // 修改引用副本的指向,不影响外部原变量 arr = new byte[2] { 0xBB, 0xCC }; } byte[] testBuff = new byte[1] { 0xAA }; ReplaceArray(testBuff); Console.WriteLine(testBuff.Length); // 输出1,证明外部数组没有被替换
你给出的示例代码完整实现如下:
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipAddr = IPAddress.Any; IPEndPoint endPoint = new IPEndPoint(ipAddr, 23000); server.Bind(endPoint); server.Listen(5); Socket client = server.Accept(); Console.WriteLine("Client endpoint : " + client.RemoteEndPoint.ToString()); byte[] buff = new byte[128]; // Receive方法仅往buff指向的内存中写数据,无需ref/out int numberOfRecievedBytes = client.Receive(buff); Console.WriteLine("Number of Recieved Bytes: " + Encoding.ASCII.GetString(buff, 0, numberOfRecievedBytes));
内容的提问来源于stack exchange,提问作者Ceremony05
相关产品推荐
相关产品推荐

