zoukankan      html  css  js  c++  java
  • 那些年我们说过的值类型与引用类型

    Overview

    FastSocket是一个轻量级易扩展的c#异步socket通信库,项目开始于2011年,经过近3年不断调整与改进,目前在功能和性能上均有不错的表现。

    项目地址:https://github.com/devhong/FastSocket.Net 

    FastSocket内置了命令行、二进制、thrift协议,基于此开发了Zookeeper, Redis, Thrift等c#异步客户端,接下来将会一一公开。

    Requirements

    .Net 4.0 or Mono 2.6 

    Example Usage 

    简单的命令行服务

    新建控制台项目,添加FastSocket.SocketBase,FastSocket.Server引用

    自定义服务实现MyService

    复制代码
    using System;
    using Sodao.FastSocket.Server;
    using Sodao.FastSocket.Server.Command;
    using Sodao.FastSocket.SocketBase;
    
    /// <summary>
    /// 实现自定义服务
    /// </summary>
    public class MyService : CommandSocketService<StringCommandInfo>
    {
        /// <summary>
        /// 当连接时会调用此方法
        /// </summary>
        /// <param name="connection"></param>
        public override void OnConnected(IConnection connection)
        {
            base.OnConnected(connection);
            Console.WriteLine(connection.RemoteEndPoint.ToString() + " connected");
            connection.BeginSend(PacketBuilder.ToCommandLine("welcome"));
        }
        /// <summary>
        /// 当连接断开时会调用此方法
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="ex"></param>
        public override void OnDisconnected(IConnection connection, Exception ex)
        {
            base.OnDisconnected(connection, ex);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(connection.RemoteEndPoint.ToString() + " disconnected");
            Console.ForegroundColor = ConsoleColor.Gray;
        }
        /// <summary>
        /// 当发生错误时会调用此方法
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="ex"></param>
        public override void OnException(IConnection connection, Exception ex)
        {
            base.OnException(connection, ex);
            Console.WriteLine("error: " + ex.ToString());
        }
        /// <summary>
        /// 处理未知命令
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="commandInfo"></param>
        protected override void HandleUnKnowCommand(IConnection connection, StringCommandInfo commandInfo)
        {
            commandInfo.Reply(connection, "unknow command:" + commandInfo.CmdName);
        }
    }
    复制代码

    Exit命令

    复制代码
    /// <summary>
    /// 退出命令
    /// </summary>
    public sealed class ExitCommand : ICommand<StringCommandInfo>
    {
        /// <summary>
        /// 返回命令名称
        /// </summary>
        public string Name
        {
            get { return "exit"; }
        }
        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="commandInfo"></param>
        public void ExecuteCommand(IConnection connection, StringCommandInfo commandInfo)
        {
            connection.BeginDisconnect();//断开连接
        }
    }
    复制代码

    App.config配置

    复制代码
    <?xml version="1.0"?>
    <configuration>
    
      <configSections>
        <section name="socketServer"
                 type="Sodao.FastSocket.Server.Config.SocketServerConfig, FastSocket.Server"/>
      </configSections>
    
      <socketServer>
        <servers>
          <server name="cmdline"
                  port="8400"
                  socketBufferSize="8192"
                  messageBufferSize="8192"
                  maxMessageSize="102400"
                  maxConnections="20000"
                  serviceType="CommandLine.MyService, CommandLine"
                  protocol="commandLine"/>
        </servers>
      </socketServer>
    
    </configuration>
    复制代码

    初始化及启动服务

    复制代码
    static void Main(string[] args)
    {
        SocketServerManager.Init();
        SocketServerManager.Start();
    
        Console.ReadLine();
    }
    复制代码

    启动服务,然后在cmd中运行telnet 127.0.0.1 8400, 运行截图如下:

    其中welcome中当连接建立时服务端发送到终端的。

    connection.BeginSend(PacketBuilder.ToCommandLine("welcome"));

    unknow command:Hello是因为没有对应的"Hello"命令实现由HandleUnKnowCommand输出的

    复制代码
    /// <summary>
    /// 处理未知命令
    /// </summary>
    /// <param name="connection"></param>
    /// <param name="commandInfo"></param>
    protected override void HandleUnKnowCommand(IConnection connection, StringCommandInfo commandInfo)
    {
        commandInfo.Reply(connection, "unknow command:" + commandInfo.CmdName);
    }
    复制代码

    当在终端中键入exit时,触发了ExitCommand.ExecuteCommand方法,服务端主动断开连接,终端退出。

     
     
    标签: fastsocket

    那些年我们说过的值类型与引用类型

    CLR支持两种类型:引用类型值类型,虽然FCL中大多的类型都是引用类型,但是我们程序员用得最多的还是值类型。引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址---也就是指向对象数据的内存地址,引用类型有以下特点:

    1.  内存必须从托管堆上分配。
    2. 堆上分配的对象都有一些额外的成员,这些成员必须初始化。
    3. 从托管堆上分配一个对象时,可能强制执行一次垃圾收集操作。

      托管堆的概念,简单的说,所有的.NET语言在分配引用类型对象时都要使用托管堆。像值类型这样的轻量级对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆。托管堆的更深入理解请看这里:http://blog.sina.com.cn/s/blog_538342930100nlmc.html

      试想如果所有类型都是引用类型,那么我们的应用程序在使用一个Int32值都要进行一次内存分配,性能会受多么大的影响,幸运的是CLR提供了更为轻量级的值类型,值类型有以下特点:

    1. 值类型在线程栈上分配。
    2. 值类型的实例没有引用类型的特殊成员:类型对象指针和同步块索引,因而不受垃圾回收器的控制,这一点很大程度的缓解了托管堆的压力,减少了应用程序在生存期内垃圾回收的次数
    3. 所有的值类型派生自System.ValueType
    4. 所有的值类型都是隐式密封的,这意味着值类型不能被继承
    5. 结构,枚举类型属于值类型

      按照国际惯例,应该上一段代码:

    复制代码
            class Aref { public Int32 x;}
    
            struct Aval { public Int32 x;}
    
            static void Main(string[] args)
            {
                Aref r1 = new Aref();
                Aval v1;
                r1.x = 1;
                v1.x = 1;
    
                Aref r2 = r1;
                Aval v2 = v1;
                r1.x = 5;
                v1.x = 6;
                Console.WriteLine(r1.x);//显示5
                Console.WriteLine(r2.x);//显示5
                Console.WriteLine(v1.x);//显示6
                Console.WriteLine(v2.x);//显示1
            }
    复制代码

      上面这段代码应该很容易看懂,这也是我们程序员面试的时候经常碰到的一个问题之一,它很好的说明了值类型与引用类型的区别。注意到上面的代码Aval v1 并没有写成 Aval v1 = new Aval(),使用new操作符可以对值类型初始化,在线程栈上分配实例,并将字段初始化为零。

      值类型能提供更好的性能,将类型声明为值类型需要满足以下条件:

    1. 类型不需要从其他类型继承
    2. 类型不会派生其他类型
    3. 类型中没有成员修改该类型的任何字段

      一个方法的返回值为值类型,那么调用者调用该方法的时候,实例中的字段会复制到调用者分配的内存中,所以值类型的实例如果过大也会对性能有损害,CLR推荐值类型的实例应该小于16字节或者更小,如果大于16字节那么该类型不应作为方法的实参传递或者作为返回值。

      值类型的主要优势在于他不作为对象在托管堆上分配,下面列出了值类型与引用类型的一些不同:

    1. 值类型对象有两种表示形式:装箱形式和未装箱形式,而引用类型总是处于已装箱形式。
    2. 由于不能将一个值类型来派生新的类型,所以不应在值类型中定义虚方法,值类型所有的方法都不能是抽象的。
    3. 创建引用类型的变量时,它被初始化为null,创建值类型变量时,所有成员都被初始化为零。
    4. 值类型的赋值会逐字段赋值,引用类型赋值只复制内存地址,所以对一个变量的操作可能影响到另一个变量。
    5. 值类型的变量生存期是方法的执行完毕,一旦定义值类型的实例的方法不再处于活动状态,为它分配的内存就会被释放,而引用类型的释放需要GC回收。

        值类型与引用类型的相互转换称为装箱和拆箱,装箱需要在托管堆上分配额外的对象,将来必须对其进行垃圾回收,所以不必要的,额外的装箱应尽量避免,否则会影响程序的性能和内存消耗,比如在一个循环中重复对一个值类型的变量进行装箱。

      又是深夜了,杭州还是那么热,“尤特” 你快些来...

     
     
    分类: CLR via C#
  • 相关阅读:
    SharePoint 2019
    SharePoint 2019 图文安装教程
    SharePoint 2016 服务器部署(七)SharePoint 和OOS 集成
    如何将域中的AD数据导入SharePoint
    SharePoint 2016 图文安装教程 后面有激活序列号、密钥分享
    Github上优秀的.NET Core项目
    SQL Server删除/创建复制订阅失败,报15517错误问题的处理
    vue-devtools 开发工具的安装
    laravel 查询数据toArray内层无法转换的问题
    KindEditor 增加html标签
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3267355.html
Copyright © 2011-2022 走看看