c#的相关笔记

参数

在方法中的参数分为形参和实参,形参就是方法定义时写的数据类型 参数名 ,实参就是调用方法实际传进的参数,c#除了形参和实参之外还有引用参数ref和输出参数out,引用参数在定义和使用时都得用ref ,输出参数也是,引用参数必须是变量,输出参数在定义时不用指定值

namespace ConsoleApplication1
{
    class RefClass
    {
        public bool Judge(ref int num)
        {
            if (num%5==0)
            {
                return true;
            }
            return false;
        }
    }
}
 class Program
    {
        static void Main(string[] args)
        {
            OutClass outClass = new OutClass();
            bool rs;
            outClass.Judge(22,out rs);
            Console.WriteLine(rs);
        }
    }

部分类

partial部分类详解,这个java里面就没有这种东西
定义部分类的形式如下:

访问修饰符 修饰符 partial class 类名 {.....}

这个部分类的作用就是看作将类拆成多个部分,为什么要拆成多个部分,内容相似或者比较多时可以采用如此的做法
部分类中可以直接使用成员变量

namespace ConsoleApplication1
{
    public partial class Course
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Points { get; set; }
    }
    public partial class Course
    {
        public void PrintCoures()
        {
            Console.WriteLine("课程编号"+Id);
            Console.WriteLine("课程名称" + Name);
            Console.WriteLine("课程学分" + Points);
        }
    }
}
			Course course = new Course();
            course.Id = 1001;
            course.Name = "c#部分类";
            course.Points = 3;
            course.PrintCoures();

部分方法

有部分类也有部分方法
部分方法就是在部分类中对方法进行扩充
使用部分方法就需要注意如下3点:

  1. 部分方法必须是私有的,并且不能使用virtual,abstract,override,new,sealed,extern等修饰词
  2. 部分方法不能有返回值
  3. 部分方法中不能使用out类型的参数
    部分方法都得使用partial关键字不然就会报错
namespace ConsoleApplication1
{
    public partial class Course
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Points { get; set; }

        //部分类的结合
        partial void PrintCoure();

        public void PrintMsg()
        {
            PrintCoure();
        }
    }
    public partial class Course
    {
        partial void PrintCoure()
        {
            Console.WriteLine("课程编号"+Id);
            Console.WriteLine("课程名称" + Name);
            Console.WriteLine("课程学分" + Points);
        }
    }
}

字符串

在c#中字符串对象是可以当作java中的数组来用的,例如下面:

string a="我爱西红柿炒鸡蛋"
//获取字符串中的西字
console.writeLine("获取的字符为{0}",a[2]);

字符串的相关方法 常见方法有:
1 Length 获取字符串的长度,即字符串中字符的个数
2 IndexOf 返回整数,得到指定的字符串在原字符串中第一次出现的位置
3 LastlndexOf 返回整数,得到指定的字符串在原字符串中最后一次出现的位置
4 Starts With 返回布尔型的值,判断某个字符串是否以指定的字符串开头
5 EndsWith 返回布尔型的值,判断某个字符串是否以指定的字符串结尾
6 ToLower 返回一个新的字符串,将字符串中的大写字母转换成小写字母
7 ToUpper 返回一个新的字符串,将字符串中的小写字母转换成大写字母
8 Trim 返回一个新的字符串,不带任何参数时表示将原字符串中前后的空格删除。 参数为字符数组时表示将原字符串中含有的字符数组中的字符删除
9 Remove 返回一个新的字符串,将字符串中指定位置的字符串移除
10 TrimStart 返回一个新的字符串,将字符串中左侧的空格删除
11 TrimEnd 返回一个新的字符串,将字符串中右侧的空格删除
12 PadLeft 返回一个新的字符串,从字符串的左侧填充空格达到指定的字符串长度
13 PadRight 返回一个新的字符串,从字符串的右侧填充空格达到指定的字符串长度
14 Split 返回一个字符串类型的数组,根据指定的字符数组或者字符串数组中的字符 或字符串作为条件拆分字符串
15 Replace 返回一个新的字符串,用于将指定字符串替换给原字符串中指定的字符串
16 Substring 返回一个新的字符串,用于截取指定的字符串
17 Insert 返回一个新的字符串,将一个字符串插入到另一个字符串中指定索引的位置
18 Concat 返回一个新的字符串,将多个字符串合并成一个字符串****

数据类型转换

数据类型转换分为两种一种为隐性转换,一种为显性转换
隐性转换经常用在整数类型和浮点数类型小类型转成大类型中,在整数中有符号的隐式转换和java是一样的,无符号的隐式转换就可以转成有符号的哦
从 sbyte 类型到 short,int,long,float,double,或 decimal 类型。
从 byte 类型到 short,ushort,int,uint,long,ulong,float,double,或 decimal 类型。
从 short 类型到 int,long,float,double,或 decimal 类型。
从 ushort 类型到 int,uint,long,ulong,float,double,或 decimal 类型。
从 int 类型到 long,float,double,或 decimal 类型。
从 uint 类型到 long,ulong,float,double,或 decimal 类型。
从 long 类型到 float,double,或 decimal 类型。
从 ulong 类型到 float,double,或 decimal 类型。
从 char 类型到 ushort,int,uint,long,ulong,float,double,或 decimal 类型。
从 float 类型到 double 类型。
Convert方法是数据类型转换中最灵活的方法,它能将任意数据类型的值转成任意数据类型

数据类型 变量名=convert.to数据类型(变量名)

Convert.ToInt16() 转换为整型(short)
Convert.ToInt32() 转换为整型(int)
Convert.ToInt64() 转换为整型(long)
Convert.ToChar() 转换为字符型(char)
Convert.ToString() 转换为字符串型(string)
Convert.ToDateTime() 转换为日期型(datetime)
Convert.ToDouble() 转换为双精度浮点型(double)
Conert.ToSingle() 转换为单精度浮点型(float)


C#装箱和拆箱思想
c#语言中数据类型分为值类型和引用类型,值类型转成引用类型被称为装箱思想,引用类型转成值类型被称为拆箱思想


正则表达式

匹配字符
1 . 匹配除换行符以外的所有字符
2 \w 匹配字母,数字,下划线
3 \s 匹配空白符
4 \d 匹配数字
5 \b 匹配表达式的开始或结束
6 ^ 匹配表达式的开始
7 $ 匹配表达式的结束
表示重复的字符
1 * 0次或者多次字符
2 ? 0次或1次字符
3 + 1次或多次字符
4 {n} n次字符
5 {n,M} n到m次字符
6 {n,} n次以上字符


数组

数组都是由连续内容位置组成的,最低的地址对应第一个元素,最高的地址对应最后一个元素
具体内容与java类似


枚举类型

具备相同属性的有限数据的集,通常用于常量定义,枚举是定义在类中的,其使用有点像类的属性就是通过**.**直接调用

访问修饰符 enum 变量名:数据类型{
值1,
值2
}
//注意上面的数据类型不能是string。。。这点有点无语

结构体

结果体是类似于类但它是值类型的,它通常定义于类或者在命名空间下进行定义中能够想要进行使用它得进行new出来,咋感觉像内部类的感觉,它有关键字struct的,例子如下:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            student stu = new student();
            stu.Name = "张三";
            stu.Age = -100;
            Console.WriteLine("学生的信息为");
            Console.WriteLine(stu.Name+":"+stu.Age);
        }
    }
    struct student
    {
        private string name;
        private int age;

        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
            }
        }

        public int Age
        {
            get
            {
                return age;
            }

            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                else
                {
                    age = value;
                }
               
            }
        }
    }
}


继承

在C#语言中仅支持单继承,这点和java很像都是解决代码的重用问题
c#继承的特点:

  • 派生类是对基类的扩展,派生类可以添加新成员,但是派生类不能够动态移除基类的成员
  • 继承是传递的,类的话就是单继承,接口的话类就可以多实现
  • 构造函数和析构函数不能被继承,其他的成员都能被继承,基类的成员的访问方式决定是否被后代访问
  • 派生类如果定义了与基类相同名称的新成员时,新成员会覆盖旧成员,但不会删除旧成员,只是不能访问(java这点相似,但是java是有办法访问旧成员(super),c#通过base.基类的成员),virtual关键标记的成员可以被派生类重写(你没有看错就重写,这时候就晕了上面不是说相同名称的新成员就好吗,C#里面是允许的哦,基类如果允许说一部分成员被重写就标记virtual,查询相关资料如果不加virtual会被当作是覆盖相当于新来的,virtual会有标签相当于扩展成员,后面这个体现的就是多态,virtual修饰的成员为属性,方法,事件,委托等)
  • 单继承
Object类

object是c#语言中最原始,最重要的类,是所有类的“祖先”,每个类都是该类的子类,这些子类是直接或间接从object中继承来的,object中有四个常用的方法equals,gethashcode,gettype,tostring
如果是想要简单比较两个对象是否相等是可以通过获取gethashcode来进行比较,但是如果相等的情况下可以根据equals来进行最后保证比较,之所以用gethashcode是因为效率高,只是比较对象的某些特征,equals可以理解为完全比较
还有一个点就是方法在进行重写的时候必须在新成员加上关键字override这时候才不会报错。。。这点和java不同,java是直接进行重写就行压根就不用写关键字


抽象abstract

关键字代表的是抽象的,该关键字能修饰类和方法,修饰的方法被称为抽象方法,修饰的类被称为抽象类
一个拥有抽象方法的类是抽象类,一个抽象类不一定有抽象方法


interface接口

接口定义的语法形式如下:

	interface 接口名称{
		接口成员
	}
  1. 接口名称
    通常是以|开头,再加上其他的单词构成,例如创建一个计算的接口,可以命名为ICompute
  2. 接口成员
    接口成员必须满足以下要求。
  • 接口中的成员不允许使用访问修饰符
  • 接口中的成员不允许使用修饰符
  • 接口中的成员不允许定义字段
  • 在接口定义的方法不能包含方法体
    总结就是有属性,方法(没有方法体)
    当一个类实现多接口和从类中继承的话就:后面加的第一个必须是类名,后面是接口名称

集合

相比较数组,集合简单的说就是数组的升级版,用使用者的角度就是可以随便你丢数据,但实际上它还是个数组只是动态的进行长度的定义和维护。

接口名称作用
IEnumerable用于迭代集合中的项,该接口是一种声明式的接口
IEnumerator用于迭代集合中的项,该接口是一种实现式的接口
ICollection.NET 提供的标准集合接口,所有的集合类都会直接或间接地实现这个接口
IList继承自 IEnumerable 和 ICollection 接口,用于提供集合的项列表,并允许访问、查找集合中的项
IDictionary继承自 IEnumerable 和 ICollection 接口,与 IList 接口提供的功能类似,但集 合中的项是以键值对的形式存取的
IDictionaryEnumerator用于迭代 IDictionary 接口类型的集合

常用集合接口如下:

类名称实现接口特点
ArrayList ICollectionIList、IEnumerable、ICloneable集合中元素的个数是可变的,提供添加、删除等方法
QueueICollection、IEnumerable、ICloneable集合实现了先进先出的机制,即元素将在集合的尾部添加、在集合的头部移除
StackICollection、IEnumerable、ICloneable集合实现了先进后出的机制,即元素将在集合的尾部添加、在集合的尾部移除
HashtableIDictionary、ICollection、IEnumerable、 ICloneable等接口 集合中的元素是以键值对的形式存放的,是 DictionaryEntry 类型的
SortedListIDictionary、ICollection、IEnumerable、 ICloneable等接口 与 Hashtable 集合类似,集合中的元素以键值对的形式存放,不同的是该集合会按照 key 值自动对集合中的元素排序

Queue队列

数据结构为先进先出,典型的排队,其常用的方法如下

属性或方法作用
Count 属性获取 Queue 实例中包含的元素个数
void Clear()清除 Queue 实例中的元素
bool Contains(object obj)判断 Queue 实例中是否含有 obj 元素
void CopyTo(Array array, int index)将 array 数组从指定索引处的元素开始复制到 Queue 实例中
object Dequeue()移除并返回位于 Queue 实例开始处的对象
void Enqueue(object obj)将对象添加到 Queue 实例的结尾处
object Peek()返回位于 Queue 实例开始处的对象但不将其移除
object[] ToArray()将 Queue 实例中的元素复制到新数组
void TrimToSize()将容量设置为 Queue 实例中元素的实际数目
IEnumerator GetEnumerator()返回循环访问 Queue 实例的枚举数

Stack类:堆栈

栈是后进先出法,,典型用于在一端为开着其他会关闭的情况下,如同瓶子,常见方法如下:

属性或方法作用
Push(object obj)向栈中添加元素,也称入栈
object Peek()用于获取栈顶元素的值,但不移除栈顶元素的值
object Pop()用于移除栈顶元素的值,并移除栈顶元素
Clear()从 Stack 中移除所有的元素
Contains(object obj)判断某个元素是否在 Stack 中
object[] ToArray()复制 Stack 到一个新的数组中

Hashtable类:哈希表(散列表)

集合中的值是都是以键值对的形式存取的
构建:

hashtable 对象名=new hashtable();

常用的属性和方法如下:

属性或方法作用
Count集合中存放的元素的实际个数
void Add(object key,object value)向集合中添加元素
void Remove(object key)根据指定的 key 值移除对应的集合元素
void Clear()清空集合
ContainsKey (object key)判断集合中是否包含指定 key 值的元素
ContainsValue(object value)判断集合中是否包含指定 value 值的元素

SortedList类

又是一个集合,它能以键值对形式存取,而且按照key值对集合中的元素排序


类与类之间的比较IComparable,IComparer

如果一个类因为需要自定义排序,可以用两种方式一种是比较对象实现IComparable接口重写compare方法,这个接口的要有泛型因为比较是建立在可对比的群体上的,需要有限制,IComparer接口用于定义比较器,同样需要定义泛型,因为不可能啥都能比。
IComparable接口如下:

class StudentOther:IComparable<StudentOther>
    {
        public StudentOther(int id,string name,int age)
        {
            this.id = id;
            this.name = name;
            this.age = age;

        }

        //学号
        public int id { get; set; }

        //姓名
        public string name { get; set; }

        //年龄
        public int age { get; set; }

        public override string ToString()
        {
            return id+":"+name+":"+age;
        }

        public int CompareTo(StudentOther other)
        {
            if (this.age>other.age)
            {
                return -1;
            }
            return 1;
        }
    }

在实际上去调用:

List<StudentOther> list = new List<StudentOther>();
list.Sort();

Icomparer接口的应用

class MyCompare : IComparer<StudentOther>
    {
        public int Compare(StudentOther x, StudentOther y)
        {
            if (x.age > y.age)
            {
                return -1;
            }
            return 1;
        }
    }

在使用的时候:

List<StudentOther> list = new List<StudentOther>();
list.Sort(new MyCompare());//将比较器传到里面就行

文件操作

Path

它是一个静态类,用于对文件路径的解析操作,它可以根据一个文件路径解析出扩展名,文件名,文件全名,修改扩展名,获取其根路径,当前文件所处的目录等
注意啊,是解析指定文件路径(字符串),修改扩展名也只是对其文件路径字符串修改而不是实际文件扩展名,如果需要对其实际文件操作的话可以使用file或fileinfo


File

它是一个用于操作实际文件的静态类,能够根据路径对文件进行操作(删除,创建,转移,复制,替换),获取文件属性,它就像是窗口程序的右键,它与FileInfo很相似,但是FileInfo是需要实现的类,它还能够向指定文件中写入信息
与Fileinfo区别是它可以非常方便的向文件里面写进数据,file就可以,file写入数据的话如果路径没有存在的话,它会自己创建而将其数据写入

 Console.WriteLine("请输入一个文件路径:");
 string path = Console.ReadLine();
 bool isExit = fileInfo.Exists;
 Console.WriteLine("指定路径文件是否存在" + isExit);
 File.WriteAllText(path, "天天洗澡皮肤好好");

向已有内容的文件中添加内容,除了可以使用File中的appendText()方法返回的streamwrite来进行添加,如果直接使用streamwrite的话会造成覆盖,还可以使用filestream类来进行后续添加内容(这里需要用到一个文件打开模式FileMode.Append,大概做法就是使用路径和这个FileMode.Append模式来进行创建一个FileStream对象)
还有一个非常注意的点在c#中将字符串变成字节可以使用System.Text.Encoding.UTF8.getByte()来进行转换,常见是utf8编码方式


委托

委托从字面上理解就是一种代理,类似于房屋中介,有业主委托中介为其出租,在C#中委托多指委托某个方法来实现具体的功能,定义委托很像方法,但它是引用类型
委托在使用时遵循三步走原则:定义声明委托,实例化委托以及调用委托。
委托有三种方式命名方法委托,多播委托,匿名委托。

命名方法委托
//声明定义
 修饰词 delegate 返回值类型 委托名(被委托方法的参数);
 //实例化委托
委托名  委托对象= new 委托名(方法名);//如果方法是实例的可以使用匿名方法.方法名方式,注意不需要加括号
//调用委托
委托对象名(参数列表);

例子如下:

public delegate double MyDelegate(double a,double b);
//在main方法中
MyDelegate myDelegate = new MyDelegate(new ComputeMore().Add);
Console.WriteLine(myDelegate(1.22, 2.33));
多播委托

多播委托可以看作是命名委托的一种扩展,命名委托只是委托一种,那么能不能动态委托呢,就像是房产中介,他们肯定是能够受理多种委托种业务的,不然人家不就被饿死吗,可以理解为动态委托,但是多播委托同时也存在一个很严重的问题,由于使用同一个委托,参数类型,返回值类型都得相等

ComputeMore computeMore = new ComputeMore();
MyDelegate myListDelegate = new MyDelegate(computeMore.Add);
myListDelegate += computeMore.Divide;
Console.WriteLine(myListDelegate(2.25, 3.66));
匿名委托

通常是A委托B,在明确知道谁委托谁的情况下进行委托
所谓的匿名是指就是不知道是哪个类,只知道委托内容,就像B知道委托内容,但不知道谁委托
具体的语法是:

//委托定义
修饰词 delegate 返回值类型 委托名 (参数列表);
//委托实现,匿名委托
委托名 委托对象=delegate{
	//代码块
}
//调用匿名委托
委托对象名(参数列表);

例子如下:

			Console.WriteLine("请输入圆的半径:");
            double r= double.Parse(Console.ReadLine());
            MyListDelegate myListDelegate = delegate
            {
                Console.WriteLine("圆的面积为:"+r*r*3.14);
            };
            myListDelegate(r);
            //定义的委托如下:
            public delegate void MyListDelegate(double a);

编译

关于c#的编译
c#语言会被编译成MSIL(中间语言)存储于程序集中,在代码执行时会使用JIT编译器将代码编译为本机代码,在托管的CLR环境下运行本地代码


静态类

最近在看c#的书籍时,看到静态类,学java的看到一脸蒙,什么鬼(个人之前的想法是类不用实现,有静态方法,私有构造函数,类名前加上sealed,当看书我才知道我想多了,不过这种构造的话,就是我可以继承和实现,不能被实现和继承),静态类就是在class关键字前面添加static关键,它的成员都是静态的,一看就是净户出身,是的它不能继承除Object类之外的类和接口,它也不能被其他类继承,看见没有妥妥的工具人,俗称绝户绝丁类,有点像无儿无女,没有亲人的孤儿,例子:
静态类—绝上绝下,而且还不能被实例化

static class TestThree
    {
        public static int a = 11111;

    }

这个我自己想的—有上绝下,它能够利用自己内部方法来进行实现自己哦

class Testagain
    {
    }
sealed class TestFour:Testagain
    {
        private TestFour()
        {

        }
        public static int a = 11111;
    }

匿名类

所谓的匿名类就是之前没有去定义类,没错就是没有定义,直接在用的时候就把它给造出来,

  var val = new {StrVal="a String",IntVal=10 };
  Console.WriteLine(val.StrVal);

base

方法后面加上:base表示先执行完基类对应的方法后再执行自己编写的代码,当有个注意的点base不能用于静态方法,能简单理解为base调动的是基类的实例,当一个派生类继承一个基类后,实例化派生类之前会先执行基类的构造方法,简单得说就是先有基类实例后有派生类,如同先有你爹再后有你,你总不能比你爹还早吧


异步

方法要变成异步的话前面必须加上async Task ,实际的异步调用逻辑前面要加上await


实践中应用到场景
将数组中的元素依据数组中的顺序分别设置到对象的属性值

根据的是反射,利用反射找到对象对应类的属性,并将通过这个反射到的属性给对象的属性赋值(个人理解相当于拿到对象其属性的地址,然后顺着地址找到其所在并将值赋给它)

		/// <summary>
        /// 转换表格类型
        /// </summary>
        /// <typeparam name="R">类类型</typeparam>
        /// <param name="dt">表格源</param>
        /// <returns>新集合</returns>
        public T CopyToList<T>(object[] dt)
        {
            Type resultType = typeof(T);
            T t = new T();
            PropertyInfo[] propertyInfos = resultType.GetProperties();
            for (int i = 0; i < dt.Length; i++)
            {
                var a = dt[i];
                propertyInfos[i].SetValue(t,  dt[i]);
            }
            return t;
        }

异步编程

一共有三个内容。一般的自定义异步模式,基于事件的异步模式,基于任务的异步模式。
一般的自定义异步模式是建立在委托的异步,委托的方法还得使用方法名.beginInvoke(输入参数,异步方法,null)开启异步,,例子如下:

private void OpenNewWindowButton_Click(object sender, EventArgs e)
        {//带参数的委托
            Func<int,bool> suncTest = (el) =>
            {
                Thread.Sleep(el);
                InvokeNewWindowTest newWindow = new InvokeNewWindowTest();
                newWindow.Show();
                return true;
            };
            //这个开启异步回调,携带输入int类型的参数
            suncTest.BeginInvoke(5000, ar => {
            	//获取异步回调的输出结果
                suncTest.EndInvoke(ar); //返回bool
                
            },null);
        }

基于事件的异步模式,先创建一个BackgroundWorker 实例,异步方法在dowork里面定义,然后等到异步方法执行dowork后还会执行runworkcompleted部分

		private void OpenEventInvokeButton_Click(object sender, EventArgs e)
        {
            string s = "";
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += (sender1, e1) =>
            {
                Thread.Sleep(2000);
                s = "基于事件的异步完成";
            };
            bw.RunWorkerCompleted += (sender1, e1) =>
              {
                  label2.Text = s;
              };
            bw.RunWorkerAsync();
        }

基于任务的异步程序
首先要知道所谓的任务就是执行方法,异步在于第三方的委托,就是由第三方去调用方法,还有就是要表明是异步就得在代理执行方法定义关键字async,执行的异步程序要用await来标明,这两个关键是精密相关的哦,一旦一方被定义时两者就缺一不可

		//定义为异步方法
 		private async static void CallerWithAsync2()
        {
        	//await来进行解除异步方法
            Console.WriteLine(await GreetingAsync("天天向上"));
        }
		//这个是定义事情哦
        static string Greeting(string name)
        {
            Thread.Sleep(3000);
            return string.Format("hello,{0}",name);
        }
		//这个是定义任务,这个任务就是用来执行上面的事情的
        static Task<string> GreetingAsync(string name)
        {
            return Task.Run<string>(()=>
            {
                return Greeting(name);
            });
        }

任务延续,这个延续针对的是任务哦,任务的ContinueWith方法就用来执行当任务执行后所调用的方法

private static void CallerWithContinuationTask()
        {
            Task<string> t1 = GreetingAsync("天天向上");
            t1.ContinueWith(t=> {
                string result = t.Result;
                Console.WriteLine(result);
            });
        }

任务组合器,一个任务就是一个方法,当要使用多个任务却只要定义一个异步方法时就可以使用这种任务组合器,利用Task类型中的WhenAll或WhenAny方法,一个是当所有任务都返回时就放回一个task,一个是当任务一个任务返回时就返回一个task,例子如下:

private async static void MultipleAsyncMethodsWithCombinatorsl()
        {
            Task<string> t1 = GreetingAsync("天天向上");
            Task<string> t2 = GreetingAsync("打井专业人");
            string [] result= await Task.WhenAll(t1,t2);
            Console.WriteLine("finished both methods\n Result 1:{0} \n Result 2:{1}",result[0],result[1]);
        }

visual studio

visual studio是一个集成开发环境,它使用Solution Explorer来看项目和解决方案,解决方案是包含相关项目的集合,项目就是构成解决方案的元素。
解决方案是用一个扩展名为.sln的文件描述的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值