unity学习笔记(2)C#基础学习

学习视频:https://b23.tv/jW9rNY 来源:b站
笔记介绍:在有编程基础上的C#学习,简单记录,便于自我复习。除C#知识外多记录了些没有学好的编程基础知识

目录

学习视频:https://b23.tv/jW9rNY 来源:b站笔记介绍:在有编程基础上的C#学习,简单记录,便于自我复习。除C#知识外多记录了些没有学好的编程基础知识

(一).NET简介

(二)变量

(三)语法

(四)数据类型

(五)枚举

(六)类和对象

(七)成员变量

代码示例1 “hello world”:输入输出函数,格式含义

代码示例2 :占位符 标准数字格式字符串 转义符

代码示例3 :数据类型转换 从字符串中获取每个字符 随机数 字符串

代码示例4 :数组 二维数组

代码示例5 :数组传参

代码示例6 :传参

代码示例7 :拆装箱

代码示例8 :字符串池 字符拼接

代码示例9 :枚举

代码示例10 :类和成员变量 属性

代码示例11:类的数组

 代码示例12:C#泛型集合 List  字典集合 Dictionary

 代码示例13:继承、静态


(一).NET简介

.NET dotnet :Microsoft 新一代多语言的开发平台,用于构建和运行应用程序。

主要由两部分组成:公共语言进行时;类库

 

编译运行过程:源代码(cs文本)---CLS编译-----中间语言(exe dll)------CLR编译------机器码01

CLS编译目的:跨语言           CLR编译目的:优化 跨平台

语言

CLS 公共语言规范(定义了.NET平台上运行的语言所必须支持的规范,避免语言特性产生错误)

类库

ASP.NET Web窗体 Web服务

Windows 窗体

ADO.NET &XML

基类库

CLR公共语言运行库(程序的运行环境,负责内存分配、垃圾收集、安全检查等)

操作系统

C# csharp :微软为.NET推出的高级编程语言。

Mono:支持在其他操作系统下开发.NET框架。

Unity借助Mono实现跨平台,核心是.NET Framework框架。

 

二)变量

容量的单位:

位:记忆体最小单位;字节:存储最小单位;网速10M指的是Mbps(兆位/秒),速率单位,相当于10/8兆字节/秒。

整数:

 

有符号

无符号

1个字节

Sbyte(-128~127)

Byte(0~255)

2个字节

Short(-32768~32767)

Ushort(0~65535)

4个字节

Int

Uint

8个字节

Long

Ulong

非整数:默认double,要加后缀

4个字节

单精度浮点数float

7位

8个字节

双精度浮点数double

15-16位

16个字节

128位数据类型decimal

28-29位

非数值型:

char

字符,2字节

存储单字符,使用单引号

 

String

字符串

存储文本,使用双引号

Bool

判断,1字节

真1假2

 

(三)语法

声明:变量类型 变量名;

命名规则:有字母、数字、下划线;

 

四)数据类型

1.类型分类

 通用类型系统CTS是.NET框架中的一个组成部分

         A值类型 :存储数据本身

                         a.结构(数值类型、bool、char)

                         b.枚举

        B引用类型:存储数据的引用(内存地址)

                         a.接口

                         b.类(string、Array、委托)

2.内存:CPU与其他外部存储器交换数据的桥梁

   分配:

           A栈区:空间小(1MB),读取速度快;用于存储正在执行的方法,分配的空间叫做栈帧用于存储方法中的参数和变量等。执行完毕后清除栈帧。

           B堆区:空间大,读取速度慢;用于存储引用类型的数据。

3.局部变量

定义在方法内部的变量;没有默认值必须设置初始值;方法被调用时存在栈里,结束清除。

局部变量中---->

               值类型:声明在栈中,数据存储在栈

               引用类型:声明在栈中,数据存储在堆中,栈中存储该数据的引用

            (分配图:关于此部分可看视频 了解空间存储,区分修改的是栈中数据还是堆中数据

垃圾回收器:GC是CLR中一种针对托管堆自动回收释放内存的服务。栈中引用判断内存是否使用,不使用则回收(null)

 

4.方法执行在栈中,执行完毕清除栈帧。

5.比较:数组比较与整数比较,注意比较对象

6.赋值

7.传参

 

五)枚举

列举某种数据的所有取值

作用:增强代码可读性,限定取值

语法:enum 名字{值1,值2,值3}。

枚举元素默认为int,准许使用byte、sbyte、short、ushort、int、uint、long、ulong。

每个枚举元素都有枚举值,默认情况下第一个枚举的值为0,后面每个枚举的值递增1,可修改。

 

六)类和对象

面向对象:一种软件开发思想。指导程序员如何分析解决问题

类:抽象概念,“类别”

对象:类的具体实例,“某类别的个体”

名词类型共性:数据成员

动词类型共性:方法成员

语法: 访问级别 class 类名

{

    .......类成员......

    字段:存储数据

    属性:保护字段

    构造函数:听过创建对象的方式,初始化类的数据成员

    方法:向类的外部提供某种功能

}

每个类都在一个独立的C#源文件中,创建新的类意味着在当前项目中产生了一种新的数据类型。

声明在栈中,对象在堆里,对象的数据成员也在堆里。

下面是代码示例10的内存分配情况,蓝色为第一部分代码 橙色为第二部分的修改情况

 

七)成员变量

定义在类中方法外的变量

特点:具有默认值;所在类被实例化后存在堆中。对象被回收时,成员变量从堆中清除;可以和局部变量重名,优先查找局部变量。

 

属性:对字段起保护作用,可实现只读、只写功能。

本质就是对字段的读取和写入方法。

属性语法:

[访问修饰符] 数据类型 属性名

{

      get{return 字段;}

      set{字段=value;}

}

通常一个公有属性和一个私有字段对应,属性只是外壳,实际上操作的是私有字段。

 

构造函数:提供了创建对象的方式,常常用于初始化类的数据成员。

如果不希望在类的外部创建对象,就将构造函数私有化。

特点:没有返回值,与类同名,只有在new的时候才能调用;一个类若没有构造函数,编译器会自动生成一个无参数构造函数。

语法:

[访问修饰符] 类名([参数列表])

{

    初始化......

}

 

this关键字:表示当前对象的引用,访问当前类成员时,使用this关键字;可以提高代码的可读性,在没有歧义的情况下可以省略。
 

结构struct:

结构属于值类型适合轻量级,类属于引用类型。

结构体不能包含五参数的构造函数,因为其自带。

其构造函数中,必须先为所有字段赋值。

 

const:常量。

 


新建项目:

VS2019创建空白解决方案;添加新建项目,选择 控制台应用(.NET Framework);

新建类:

右键添加类


代码示例1 “hello world”:输入输出函数,格式含义


//引入命名空间
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

//Ctrl+A全选
//Ctrl+K+F 排版
//Ctrl+K+C 注释
//Ctrl+K+U 取消注释


//定义命名空间【类的住址】;逻辑划分,避免重名
namespace Day01
{
    //定义类【做工具】
    class Program
    {
        //定义方法【做功能】;程序的入口方法
        static void Main(string[] args)
        {
            //console是类【工具】;WriteLine/Readline是方法【动词的功能】;Title是属性【名词的修饰】;类.方法()调用语句【使用某个类中的功能】;
            
            Console.Title = "My first cs";
            Console.WriteLine("hello world");//控制台.写一行(“”);将括号里面的文本写入控制台

            string input = Console.ReadLine();
            Console.WriteLine("hi" + input);
            Console.Write("welcom");//不换行输出


            Console.ReadLine();//控制台.读一行;暂停程序直到输入回车;将用户在控制台输入的文本,读到程序中来
        }
    }
}

代码示例2 :占位符 标准数字格式字符串 转义符


namespace Day01
{
    class Program
    { 
        static void Main(string[] args)
        {
            //占位符
            string gunName="AK";
            string ammoCapacity = "30";
            string str=string.Format("枪的名称:{0},容量: {1}",gunName, ammoCapacity);//str的格式为format(); 占位符{位置的编号}
            Console.WriteLine(str);
            //标准数字格式字符串
            Console.WriteLine("金额:{0}", 10);//金额:10
            Console.WriteLine("金额:{0:c}",10);//金额:¥10.00
            Console.WriteLine("金额:{0:d2}", 5);//金额:05【不足两位0填充】
            Console.WriteLine("金额:{0:f1}", 1.26);//金额:1.3【根据指定精度显示】
            Console.WriteLine("金额:{0:p}", 0.1);//金额:10.00%【百分数及其精确位数】.
            Console.WriteLine("金额:{0:p0}", 0.1);//金额:10%
            Console.WriteLine("金额:{0:p1}", 10);//金额:10.0%
            //转义符 改变字符原始含义 \"【"】  \'【'】 \0【 】
            Console.WriteLine("我爱\"unity\"!!");
            // \r\n【回车换行】 \t【水平制表格】

            Console.WriteLine("我爱\r\nunity!!");

        }
    }
}

代码示例3 :数据类型转换 从字符串中获取每个字符 随机数 字符串


namespace Day01
{
    class Program
    {
        static void Main(string[] args)
        {
            //Parse转换 :string转换为其他数据类型
            //待转数据必须“像”该数据类型
            string strNumber = "12";  
            int strNumber1 = int.Parse(strNumber);

            //任意类型转换为string类型
            int number = 10;
            number.ToString();

            //从字符串中获取每个字符
            #region  vs专用,不是cs的语法 方便折叠
            string str1 = "123";
            char c1 = str1[0];//'1'
            string s1 = c1.ToString();//"1"
            #endregion

            //隐式转换 由小范围到大范围的自动转换
            byte b3 = 100;
            int i3 = b3;//i3=100

            //显示转换 由大范围到小范围的强制转换 可能发生精度丢失
            int i4 = 100;
            byte b4 = (byte)i4;//i3=100
            
            //多种变量参与计算 结果自动向较大的类型 快捷运算符不做类型提升,超出为
            float number1 = 1;
            double number2 = 2;
            int result = (int)(number1 +number2);

            //随机数
            Random random = new Random();//创建一个随机数工具 
            int rnumber = random.Next(1, 101); //产生一个随机数

            //字符串插入
            string str = "myname";
            str.Insert(4, "?");

            //查找指定字符在字符串中的索引
            int index=str.IndexOf('y');//index=1

            //其他字符方法
            str.Replace("my","you");//替换
            str.Remove(2);//从第二个开始都删掉
            str.StartsWith("myna");//比较开头一样与否
            str.Contains("name");//是否包含
        }


        
    }


}

代码示例4 :数组 二维数组



namespace Day01
{
    class Program
    {//数组
        static void Main(string[] args)
        {
            
            //声明
            int[] a;
            //初始化 new 数据类型[容量]
            a = new int[6];
            //通过索引读写每个元素
            a[0] = 1;
            //声明并赋值
            float[] b = new float[3] {1,2,3};
            //循环遍历数组
            for(int i = 0; i < a.Length; i++)
            {
                Console.WriteLine(a[i]);
            }
            //foreach 从头到尾依次读取数组元素
            foreach(int item in a)
            {
                Console.WriteLine(item);
            }

            //推断类型:根据所赋数据,推断类型
            var v1 = 1;//int

            Array arr = new int[2];

            //声明object类型 赋值任意类型
            object o1 = 1;

            //数组方法
            //indexof 查找元素
            int[] arr2 = new int[]{ 1, 2, 3, 4 };
            int index = Array.IndexOf(arr2, 2);//index=1

            //复制 克隆 排序
            int[] arr3 = new int[] { 5, 6, 7, 8 };
            Array.Copy(arr2, arr3, 2);//从arr2第一个元素选取两个复制到arr3
            object obj=arr3.Clone();//克隆了一个数组arr3给obj
            Array.Sort(arr2);//对arr2进行从小到大排序
            Array.Reverse(arr2);//反转 从大到小排序  

            //二维数组
            int[,] map = new int[4, 4] { { 2, 2, 1, 1 }, { 1, 1, 2, 2 }, { 3, 3, 4, 4 }, { 4, 4, 3, 3 } };
        }

    }
}

代码示例5 :数组传参


namespace Day01
{
    class Program
    {
        //数组传参
        static void Main(string[] args)
        {
          
            Add(new int[] { 1, 2, 3, 4 });//没有params时传递
            //params 参数数组 对于方法内部而言就是一个普通数组
            //对于方法外部(调用者)而言 是可以传递数组,传递一组数据类型相同的变量集合
            //甚至可以不传递参数
            Add(1, 2, 3, 4);
            Add();
        }

        private static int Add(params int[] arr)
        {
            int arr1 = arr[1];
            return arr1;
        }
    }
}

代码示例6 :传参


namespace Day01
{
    class Program
    {
        //数据类型
        static void Main(string[] args)
        {
            //值类型                  引用类型
            //int bool char        string Array
            //方法执行在栈中 所以在方法中声明的变量都在栈中
            //
            int b = 2;
            int a = 1;
            int[] arr = new int[] { 1 };
            Fun1(a, arr);//传递实参变量存储的内容
            Fun2(ref a);
            Fun3(out a);

            Fun4(a, out b);//组合情况

            //tryparse方法,返回2个结果 out:转换后的结果 返回值:是否可以转换
            int result;
            bool re=int.TryParse("250", out result);//判断是否可以转int

        }
        //值参数:按值传递-传递实参变量的存储的内容
        //作用是传递信息
        private static void Fun1(int a,int[] arr)
        {
            a = 2;
        }
        //引用参数:按引用传递-传递实参变量自身的内存地址,方法内部修改
        //作用是改变数据
        private static void Fun2(ref int a)
        {
            a = 3;
        }
        //输出参数:按引用传递-传递实参变量自身的内存地址,方法内部修改
        //区别1:方法内部必须为输出参数赋值
        //区别2:输出参数传递之前可以不赋值
        //作用是接收结果
        private static void Fun3(out int a)
        {
            a = 4;
        }

        private static void Fun4(int a,out int b)
        {
            a = 4;
            b = 3;
        }
    }
}

代码示例7 :拆装箱


namespace Day01
{
    class Program
    {
        
        static void Main(string[] args)
        {
            //拆装箱
            //装箱操作:值类型到object类型的隐式转换
           //内部机制: 到堆里面开辟内存空间 将值类型的数据复制到堆中 返回堆中新分配对象的地址
            //int 同步块索引 类型对象指针 三步 相对来讲“最”消耗性能
            int a = 1;
            object o = a;
            //拆箱操作:从object类型到值类型或从接口类型到实现该接口的值类型的显示转换
            //内部机制:判断给定类型是否是装箱时的类型 返回已装箱实例中属于原值类型字段的地址
            //消耗性能

            int b = (int)o;

            int num = 0;
            string str = num.ToString();//没有拆装箱

            string str2 = "" + num;//有拆装箱  相当于string.Concat("", num); 形参object ,实参int类型

        }
        //形参object ,实参传递值类型,则装箱
        //可以通过重载、泛型 避免
        private static void Add(object obj)
        {
            
          
        }
    }
}

代码示例8 :字符串池 字符拼接


namespace Day01
{
    class Program
    {

        static void Main(string[] args)
        {
            string s1 = "hi";//写死的字符串常量
            string s2 = "hi";
            bool r1 = object.ReferenceEquals(s1, s2);//true  两者是同一个字符串

            string s3 = new string(new char[] { 'h', 'i' });
            string s4 = new string(new char[] { 'h', 'i' });
            bool r2 = object.ReferenceEquals(s3, s4);//false  两者不是同一个字符串

            //字符串池
            //写死的字符串常量放入字符串池,再创的时候查找有没有相同的文本“hi”,如果有则返回对象引用
            //提高利用率

            //字符串的不可变性
            s1 = "oh";//重新开辟空间存储新字符串 替换栈中引用 防止其他对象内存被破坏

            object o1 = 1;
            o1 = 2.0;//重新开辟空间存储


            //字符拼接
            //方法一:每次拼接会产生新的对象,替换引用,产生大量垃圾
           
            string strNumber = "";
            for (int i = 0; i < 10; i++)
            {
                strNumber = strNumber + i.ToString();
            }


            //方法二:StringBuilder 可变字符串,可以在原有空间上进行修改 
            //适用于频繁对字符串操作
            StringBuilder builder = new StringBuilder(10);//10 尽量准确 超过的话会新开辟一个空间拷贝原有的,进的来但是会产生更大的垃圾
            for(int i = 0; i < 10; i++)
            {
                builder.Append(i);
            }
            string result = builder.ToString();
            builder.Insert(0,"builder");//不需要s1=s1.Insert(); 可以直接在原有空间修改
            //builder.Replace();
            //builder.Remove();
        }
    }
}

 


代码示例9 :枚举



namespace Day01
{
    class Program
    { 
        [Flags]//允许多选的标记
        public enum Move : int
        {
            Up = 0,//1
            Down = 1,//2
            Left = 2,//4
            Right = 3//8
        }
        static void Main(string[] args)
        {
            MoveStyle(Move.Down);//
            MoveStyle(Move.Down|Move.Left);//选择多个枚举值,运算符| 按位运算 1个为1则为1
            //条件:
            //1.任意多个枚举值做按位运算的时候,结果不能与其他枚举值相同(所以最好用2的n次方赋值)
            //2.定义枚举时候,使用【Flags】特性修饰


            //数据类型转换
            //int=>enum
            Move style2 = (Move)2;
            MoveStyle(style2);
            //enum=>int
            int number =(int) (Move.Up|Move.Down);

            //string=>enum
            Move style3=(Move)Enum.Parse(typeof(Move), "Up");

            //enum=>string
            string strEnum = Move.Left.ToString();

        }
        private static void MoveStyle(Move style)
        {
            if ((style & Move.Down)== Move.Down)//包含情况 用按位与& 
            {
                Console.WriteLine("Down");
            }
            if ((style & Move.Up) == Move.Up)
            {
                Console.WriteLine("Up");
            }
            if (style == Move.Left)
            {
                Console.WriteLine("Left");
            }
            if (style == Move.Right)
            {
                Console.WriteLine("Right");
            }

        }

    }
}

代码示例10 :类和成员变量 属性


//类中代码
namespace Day01
{
    class Wife
    {
        //数据成员

            //不是所有编程语言中都有private 便于统一
        private string sex;
        private int age;
        private string name;

       //属性:保护字段 本质就是2个方法
       public string Name
        {
            //读取时保护
            get
            { return name; 
            }
            //写入时保护 Value要设置的数据
            set
            {
                this.name = value;
            }
        }

        public int Age
        {
            //读取时保护
            get
            {
                return age;
            }
            //写入时保护 Value要设置的数据
            set
            {
                if (value <= 19)
                    this.age = value;
                else
                    throw new Exception("No!");
            }
        }


        //构造函数
        public Wife(string name,int age):this(name)//执行wife(name);
        {
          //  this.name = name;//构造函数如果为字段赋值,属性中的代码块不会执行
            this.Age = age;
        }
        public Wife(string name):this()//执行wife();
        {
            //调用无参数的构造函数
            this.name = name;
            
        }
        public Wife()
        {
            Console.WriteLine("有wife被创建啦");
        }


        //方法成员
        public void SetName(string name)
        {
            //this这个对象(引用)
            this.name = name;
        }
        public string GetName()
        {
            //this这个对象(引用)
            return name;
        }
        public void SetAge(int age)
        {
            //this这个对象(引用)
            if (age <= 19)
                this.age = age;
            else
                throw new Exception("No!");
        }
        public int GetAge()
        {
            //this这个对象(引用)
            return age;
        }
    }
}

namespace Day01
{
    class Program
    {
        static void Main(string[] args)
        {
            /****第一部分*****/
            //声明Wife类型的引用
            Wife wife01;
            //指向wife类型的对象(实例化wife类型对象)
            wife01 = new Wife();
            wife01.SetName("Alice");
            wife01.SetAge(18);
            Console.WriteLine(wife01.GetName());
            Console.WriteLine(wife01.GetAge());


            /****第二部分*****/
            Wife wife02 = wife01;
            wife02.SetName("Linda");
            Console.WriteLine(wife01.GetAge());

            /****第三部分*****/
            Wife wife03 = new Wife();
            wife03.Name = "selina";//执行Name的set
            wife03.Age = 30;//执行Age的set
            Console.WriteLine(wife03.Name);//执行Name的get

            /****第四部分:构造函数*****/
            Wife wife04 = new Wife("Cindy",25);
            Wife wife05 = new Wife("Baby", 24);

        }
      

    }
}

代码示例11:类的数组


namespace Day01
{
    class Program
    {
        static void Main(string[] args)
        {
            Wife[] WifeArray = new Wife[5];//初始化必须指定大小
            WifeArray[0] = new Wife("01", 40);//读写元素必须通过索引
            WifeArray[1] = new Wife("02", 41);
            WifeArray[2] = new Wife("03", 42);
            WifeArray[3] = new Wife("04", 43);
            WifeArray[4] = new Wife("05", 44);
        }


    }
}

 代码示例12:C#泛型集合 List  字典集合 Dictionary


namespace Day01
{
    class Program
    {
        
        static void Main(string[] args)
        {
            //C#泛型集合 List<数据类型> 根据索引查找
            List<Wife> list1 = new List<Wife>(2);
            list1.Add(new Wife());

            //list1.Insert();
            //list1.Remove();
            Wife wife2 = list1[0];
            //读取所有元素
            for(int i = 0; i < list1.Count; i++)
            {
                Wife w = list1[i];
            }

            //字典集合 根据键值查找
            Dictionary<string, Wife> dic = new Dictionary<string, Wife>();
            dic.Add("an", new Wife("angel",55));
            Wife anWife = dic["an"];
        }
    }
}

 代码示例13:继承、静态



namespace Day01
{
    class Program
    {
        static void Main(string[] args)
        {
            //----继承--------------------------------//
            //子类可以使用父类成员 父类只能使用父类
            //子类继承了父类的成员 但是有访问级别的限制 private用不了
            student stu01 = new student();
            stu01.Name = "";

            //父类型的引用指向子类型的对象 只能使用父类成员
            Person person01 = new student();
            student stu02 = (student)person01;//需要访问子类的成员需要强制类型转换
            int a =stu02.Score;
            //Teacher teacher01 = (Teacher)person01;//异常 
            //Teacher teacher01 = person01 as Teacher;//异常 

            //----static---------------------------//
            //静态类、静态方法、静态字段

            //静态成员变量:属于类 只有一份 早出现; 实例成员变量属于对象,创建初始化,每个对象一份
            //静态类:不能实例化,只能包含静态成员,静态类不能被继承但是静态方法、属性可以被
            


            student stu11 =new student();//0=>1
            student stu12=new student();//0=>1
            student stu13 = new student();//0=>1
            Console.WriteLine(stu13.InstantCount);//1//原因:每个对象里面都有一个instantcount 
            Console.WriteLine(student.StaticCount);//3 

            //----结构体--------------------------------//
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Day01
{
    class Person
    {
        public string Name { get; set; }
        //私有 无法使用
        private string sex;
        //仅仅本家族使用
        protected int age;
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Day01
{
    class Teacher
    {
        public int Salary { get; set; }
  
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Day01
{
    class student : Person
    {
        //prop+tab+tab
        public int Score { get; set; }
        //实例成员
        public int InstantCount;
        //静态成员
        public static int StaticCount;
        //实例构造函数作用:提供创建对象的方式,初始化累的实例数据成员
        public student()
        {
            InstantCount++;
            StaticCount++;
        }

        //静态构造函数
        //作用:初始化类的静态数据成员
        //执行时机:类加载的时候调用一次
        static student()//静态构造函数不能有访问级别
        {
          
        }
        //静态方法
        static public void fun2()       
        {
            //非静态字段 要求对象引用 否则只能访问静态成员   
            //
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值