C#基本语法学习笔记(C++基础)第二部分

C#基本语法学习笔记(C++基础)第二部分

本篇文章是针对于有C++基础的同学对C#的快速了解即入门,不适合零基础的小伙伴,本文为第二部分,如果没有看过第一部分请在文章末尾链接观看第一部分

13.访问修饰符

与C++类似,存在部分扩展

  • public:所有对象都可以访问;
  • private:对象本身在对象内部可以访问;
  • protected:只有该类对象及其子类对象可以访问
  • internal:同一个程序集的对象可以访问;
  • protected internal:访问限于当前程序集或派生自包含类的类(不常用)
  • private protected:只有在其声明的程序集内,通过相同class中的代码或派生自该class的类型,才能访问类型或成员(不常用)

访问修饰符图解

14.继承

1.构造方法

和C++一样,存在默认构造函数,如果程序员不进行编写构造函数,那么其存在一个默认构造函数。也可以自行编写有参构造函数。

也可以使用this关键字进行选择变量。

class Person
{
public string name;
public int age;

//构造方法
public Person(string name,int age)
{
this.name = name;
this.age = age;
}
}

2.析构函数

同C++析构函数形式相同

~Person()
{
}

3.继承

class Person
{
private string name;//子类中无法访问
public int age;

}
class XiaoLi:Person//继承语句Person为基类
{

}

若基类也就是父类中存在有参构造或者是非默认构造,子类在继承中也需要非默认构造,这点也类似于C++中的子类初始化

class Person
{
private string name;//子类中无法访问
public int age;
//构造方法
public Person(string name,int age)
{
this.name = name;
this.age = age;
}
}
class XiaoLi:Person
{
public XiaoLi(string name,int age):base(name,age)//子类通过base的方式初始化基类
{

}

}

15.多态、重载与重写

多态往往表现为“一个接口,多个功能”!

主要表现形式:

1.父类变量指向子类

2.函数重载

1.父类变量指向子类

using System;

namespace ConsoleApp2
{
    class Person//基类(父类)
    {
        public virtual void run()//虚函数
        {
            
        }
    }
    class xiaoLi:Person//小李子类继承于基类Person
    {
        public override void run()//子类中虚函数的重写
        {
            base.run();//先调用父类run()函数,可以删除
            Console.WriteLine("小李跑步");
        }
    }
    class xiaoWang:Person//小王子类继承于基类Person
    {
        public override void run()
        {
            base.run();//先调用父类run()函数,可以删除
            Console.WriteLine("小王跑步");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            xiaoLi xl = new xiaoLi();//小李实例化
            xiaoWang xw = new xiaoWang();//小王实例化

            Person p = xl;//p指向xl
            p.run();//此时通过p.run()运行的是小李的run函数
            p = xw;//p指向xw
            p.run();//此时通过p.run()运行的是小王的run函数

        }

    }
}

上述代码中父类p通过改变指针的指向使得run函数拥有了不同的功能,从而实现了多态。

2.函数重载

    class Person
    {
        public void run()//run函数
        {
            
        }
        public void run(string name)//重载1
        {

        }
        public void run(int age)//重载2
        {

        }
    }

函数名相同,但参数不同,可以存在多种形式,即为函数重载,通过传入相应的参数来调用对应的函数。

16.属性

在C++中为了避免变量值被随意的修改,通常类中的变量我们会设为private私有,那么外界就无法访问这个变量了,为了解决这个问题在C++中我们经常会以如下的形式来解决:

class Perosn
{
public:
string getName()
{
return name;
}
void setName(string name)
{
this.name  = name;
}

private:
string name;
}

通过编写set和get函数的方式进行值的设置与读取,那么上述代码更改一下格式,仍然可以在C#中进行使用。

但是,因为这个函数非常常用,C#就提供了更为简便的编写方式。

using System;

namespace ConsoleApp2
{
    class Person
    {

        private string? name;//私有变量
        public string? Name//共有Name属性,需要和变量不同名
        {
            get//get访问器,若不存在则不可读
            {
                return name;//返回name值
            }
            set//set访问器,若不存在则不可写
            {
                name = value;//设置name值,传进来的参数默认为value,所以只需进行简单的赋值即可
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            p.Name = "小王";//通过=号进行赋值,不可使用函数调用的方式
            string name = p.Name;//p.Name返回name的值
            Console.WriteLine(name);
        }

    }
}

17.静态

与C++几乎一致

static关键字声明静态函数或变量,静态函数只能调用静态变量,静态变量是属于类的各个实例之间数据共享。

using System;

namespace ConsoleApp2
{
    class Person
    {
        public static string? name;//name为静态成员,属于Person类,而不是属于某个实例
        public string? Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Person xl = new Person();
            xl.Name = "小李";//name为小李
            Person xw = new Person();
            Console.WriteLine(xw.Name);//输出xw的name仍然为小李,实例之间数据共享。

        }

    }
}

上述主函数中是通过实例点属性获取静态成员的数值和设置数值的,那么因为静态成员是属于Person类的,如果不存在属性,则应通过”类.成员名“

来进行调用的。

using System;

namespace ConsoleApp2
{
    class Person
    {
        public static string? name;
    }


    class Program
    {
        static void Main(string[] args)
        {
            Person xl = new Person();
            Person.name = "小李";//通过类名来进行调用
            Person xw = new Person();
            Console.WriteLine(Person.name);

        }

    }
}

静态成员的一些特点

  • 静态成员随着类的加载而加载,无论一个类拥有多少个实例,静态成员永远都只有一份副本。
  • 静态方法可以被重载
  • 静态成员由“类名.变量名”语法进行调用。
  • 静态方法只能访问静态成员,实例方法可以访问实例成员变量和实例方法,也可以访问静态成员。

18.抽象

  • 抽象类不能使用new关键字进行实例化
  • 抽象方法必须放在抽象类里,class前面加修饰符abstract
  • 抽象方法返回值前面也需要加修饰符abstract
  • 抽象发放没有方法体
  • 抽象方法必须被子类重写并实现,或使其子类已就位抽象类
  • 抽象方法的访问修饰符不能使private,不然就无法被子类访问了,也就无法重写了
    abstract class Enemy//抽象类
    {
        public abstract void Attack();//抽象方法
    }
    class Zombie : Enemy//子类
    {
        public override void Attack()//重写抽象类
        {
            Console.WriteLine("挠人");
        }
    }
    class Dog : Enemy
    {
        public override void Attack()
        {
            Console.WriteLine("咬人");
        }
    }

虽然上述过程也可以通过virtual虚函数重写来实现,但是当代码量足够多时,你可能会忘记虚函数是不应该被实例化的,就会导致问题出现。

而抽象类,是不可以进行实例化的,有效的避免了这问题。

19.接口

接口是一组包含了类或结构可以实现的功能的定义,使得这些类或结构可以保持一致的形式与特征。

接口也为C#提供了多继承的功能实现。

特点:

  1. 接口可以包含事件、索引器、方法和属性,不能包含字段
  2. 接口是引用类型
  3. 接口只包含了功能的定义,不包含功能的实现
  4. 接口默认为public 不可以修改
  5. 接口被实现后,就必须实现接口中的所有成员内容
    class Person
    {
        public void eat()
        {

        }
    }

    interface wolf
    {
        void attack();
    }

    class wolfPerson:Person,wolf
    {
        public void attack()
        {
            Console.WriteLine("攻击");
        }
    }

20.泛型

类似于C++中的模板

public static void Swap<T>(ref T value0,ref T value1)
{
T temp = value0;
value0 = value1;
value1 = temp;
}

使用泛型可以重用代码,保护类型的安全以及提高性能

降低强制转换或装箱操作的成本和风险

可以对泛型参数进行限定以访问特定数据类型的方法

多泛型

void swap<T,U>(T p0,U p1)
{
}

泛型where约束,避免参数传入出错

约束 说明
where T:struct 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。
where T:class 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
where T:new () 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
where T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
where T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
where T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.
void swap<T,U>(T p0,U p1) whrere U: struct where T:Computer,new()
{
}

21.集合

集合类似于容器,可以通过索引访问集合成员,也可以通过“键”来进行访问,集合的大小可以动态调整,可以在运行时添加或删除元素。

集合可以分为两类:泛型集合与非泛型集合(使用较少)

泛型集合类一般位于System.Collections.Generic名称空间中,

非泛型集合类一般位于System.Collections名称空间。

此外,System.Collections.Specialized名称空间中也包含一些有用的集合类。

1.数组ArrayList非泛型集合

  • 动态的增加和减少元素
  • 实现了ICollecton和IList和IEnumerable接口
  • 灵活的设置数组的大小
  • 不安全的集合类型
  • 其元素为值类型时,效率不高(装箱和拆箱消耗性能)

以下为代码示例:

using System;
using System.Collections;

namespace ConsoleApp2
{


    class Program
    {
        static void Main(string[] args)
        {
            ArrayList array = new ArrayList();
            //依次添加元素
            array.Add(0);
            array.Add(1.1f);
            //插入元素
            array.Insert(0, "新元素");
            //删除
            array.Remove(1.1f);
            array.RemoveAt(0);
            //判断某元素是否存在
            bool res = array.Contains("新元素");
            //清空
            array.Clear();
            int num = array.Count;//数量

        }

    }
}

2.堆栈Stack非泛型集合

先进后出。

线性结构,即每个节点有且只有一个前驱结点和一个后续结点。

容量自动分配添加。

可以接受null作为有效值。

允许重复的元素。

不安全的数据结构。

其泛型为Stack

using System;
using System.Collections;

namespace ConsoleApp2
{


    class Program
    {
        static void Main(string[] args)
        {
            //堆栈
            Stack stack = new Stack();
            //入栈
            stack.Push(1);
            stack.Push("2");
            //出栈
            object? num = stack.Pop();
            //仅获取最上面的内容而不出栈
            num = stack.Peek();
            int num1 = stack.Count;

        }

    }
}

3.队列Queue非泛型集合

先进先出

using System;
using System.Collections;

namespace ConsoleApp2
{


    class Program
    {
        static void Main(string[] args)
        {
            //队列
            Queue queue = new Queue();
            //入队
            queue.Enqueue(1);
            queue.Enqueue(1.1f);
            //出队
            object? obj = queue.Dequeue();
            //获取队首元素,但是不出队
            obj = queue.Peek();
        }

    }
}

4.哈希表hash非泛型集合

处理和表现类似key-value的键值对组合

key和value都是obkect类型

key值必须唯一,区分大小写

value可以是值类型变量,也可以是对象

using System;
using System.Collections;

namespace ConsoleApp2
{


    class Program
    {
        static void Main(string[] args)
        {
            //创建hash表
            Hashtable hashtable = new Hashtable();
            //添加对应的键值对
            hashtable.Add("name", "xiaobai");
            hashtable.Add("score", 100);
            hashtable["age"] = 18;//第二种方法
            //查询是否包含某个键或值
            hashtable.ContainsKey("name");
            hashtable.ContainsValue("xiaobai");
            //移除
            hashtable.Remove("name");
            object? score = hashtable["score"];
        }

    }
}

5.数组(泛型)

using System;
using System.Collections.Generic;

namespace ConsoleApp2
{


    class Program
    {
        static void Main(string[] args)
        {
            //泛型创建,其余用法与非泛型一致
            List<int> list = new List<int>();

        }

    }
}

6.堆栈泛型

//泛型创建,其余用法与非泛型一致
            Stack<int> stack = new Stack<int>();
            stack.Push(2);

7.队列泛型

  //泛型创建,其余用法与非泛型一致
            Queue<int> queue = new Queue<int>();
            queue.Enqueue(2);

8.字典(哈希表)泛型

//泛型创建,其余用法与非泛型一致
            Dictionary<string, int> dic = new Dictionary<string, int>();
            //添加键值对
            dic.Add("xiaoli", 22);
            dic["xiaohei"] = 24;
            //取值
            int value;
            dic.TryGetValue("xiaohei", out value);//尝试取值,避免直接取值导致的程序崩溃

22.委托

C#中的委托类似于C++中的函数指针。

委托是一种可以存放方法的自定义类型。

示例1:

using System;
using System.Collections.Generic;

namespace ConsoleApp2
{


    class Program
    {
        //声明一个委托类型
        public delegate int myDelegate(int x, int y);
        static int add(int a, int b)
        {
            return a + b;
        }
        static int sub(int a, int b)
        {
            return a - b;
        }
        static void Main(string[] args)
        {
            //声明一个委托变量
            myDelegate mydel;
            //委托保存方法
            mydel = add;
            //使用
            mydel(3, 4);
            mydel = sub;//改变存储
            mydel(3, 5);

        }

    }
}

示例2:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace ConsoleApp2
{
    class Person
    {
        //委托类型
        public delegate void doSomething();

        
        public void run(doSomething dosomething)//参数中声明一个委托变量
        {
            dosomething();//执行
        }
    }

    class Program
    {
        static void eat()
        {
            Console.WriteLine("吃饭");
        }
        static void play()
        {
            Console.WriteLine("玩");
        }
        static void Main(string[] args)
        {
            Person person = new Person();//实例化一个person类
            person.run(eat);//委托运行eat
            person.run(play);//委托运行play

        }

    }
}

1.委托链-多播

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace ConsoleApp2
{
    class Person
    {
        //委托类型
        public delegate void doSomething();

        
        public void run(doSomething? dosomething)//参数中声明一个委托变量
        {
            dosomething();//执行
        }
    }

    class Program
    {
        static void eat()
        {
            Console.WriteLine("吃饭");
        }
        static void play()
        {
            Console.WriteLine("玩");
        }
        static void Main(string[] args)
        {
            Person person = new Person();//实例化一个person类
            Person.doSomething? dosomething;//实例化一个委托变量
            dosomething = eat;//等于函数eat
            dosomething += play;//添加函数play
            dosomething -= play;//删除函数play
            //匿名函数添加
            dosomething += delegate ()
            {
                Console.WriteLine("睡觉");
            };
            //lambda表达式方式添加,自我感觉很像JavaScript中的箭头函数
            dosomething += () =>
            {
                Console.WriteLine("lambda表达式");
            };

            person.run(dosomething);//委托运行dosomething

        }

    }
}

基本语法完结!

文章内容是根据书籍《新印象 Unity 2020游戏开发基础与实战》随书附赠的C#入门讲解视频对于存在C++基础的同学总结而来,节省部分时间,同时也作为本人的学习笔记。如有错误或不足之处还请联系我指出进行修改,文章内容存在部分本人自我理解,具体标准内容请以官方文档为主。

© 版权声明
THE END
喜欢就支持一下吧
点赞17 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容