Csharp基本语法速通

ooowl
  • 游戏开发
  • dotnet
About 12 min

Csharp基本语法速通

平台版本

.Net Framework 出得最早,只能用在Windows平台上
微软把.NETopen in new window Framework 跨平台的部分单独拿出来,就是.NETopen in new window Standard,它是一套标准库.Netopen in new window Core就是 以它为标准开发的版本。
之后.Netopen in new window Core 3.1之后 就改名成统一大版本.Netopen in new window 5、6、7、8 而 .Net Framework 在4.8.1之后 就没有发过新版本,.Net 6和8 都有3年长期支持年限,速度快、支持跨平台,使用MIT协议开源

也不知道该放哪

装箱比拆箱更耗性能,装箱需要在堆上分配内存 在unity中,脚本是挂载在unity sense下的unity对象上才能执行的? 所有派生自MonoBehaviour的类都可以直接(拖拽)到unity对象上挂载

脚本名必须和类名一样,否则是挂载不上去的

use UnityEngine;
Debug.Log("I am a log"); // 还是常规的日志级别
在unity中使用日志,在unity中的Console窗口中显示,但是右上角有级别过滤,打印不出来的时候看看是不是手贱过滤了

驼峰命名:

int HpValue =1000; // 驼峰+语义,其实驼峰有很多种,C#的习惯来看这样比较好,需要可以再查
int iHpValue= 1000; // 带上了数据类型,有更多信息
char a='a'// char 是单引号,单字符

打印字符串的时候尽量format而不是直接拼接,因为字符串是不可变变量,更占内存

// 常用的前缀:
// str = string 
// ch= char
// i = int
// ll = longlong
// b or is =bool true false

C#的数组内存是线性连续的,且虽托管但是直接访问内存 C#的数组长度是整体长度,不是一维的长度,如果要取每个维度的长度,就需要getLength(n)

联合调试的时候首先要在Rider里面进行断点,然后unity里面也要运行程序才进入debug

unity设置个人偏好: play mode的全球变灰很难受直接选全白就不会有影响了,然后注意play的时候上面蓝色的播出箭头 search->play mode->play mode tint

unity中的钩子函数

using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using System;  
  
public class parc9_func : MonoBehaviour  
{   public int iHp = 1000; // MonoBehaviour类的public变量可以直接在unity中拖拽修改
    // Start is called before the first frame update  # 初始化的时候会调用一次 执行完会自动GC一次
    void Start()  
    { 
    }  
    // Update is called once per frame  # 每一帧都被调用
    void Update()  
    {            
    }
}

公开仓库open in new window

int a1 = 10;  
int a2 = 10;  

int a3 = a1++; // 先赋值再调用++所以是10
int a4 = ++a2; // 先调用++再赋值所以是11
Console.WriteLine(a3);  //10
Console.WriteLine(a4);  //11

readonly和const冲突基本自定义类型和自定义类型 readonly不允许非构造函数或声明的时候初始化

const static的区别 const 本质上是保持 必须赋初始值,并且只能通过类来访问不能通过变量访问 static用于实现单例或者缓存等 static分配于堆上,实际上是可以被改变的,也是必须要分配初始值,static只能在static中定义

静态构造函数只被调用一次,无论子类或本类示例化多少次 静态类不允许在外部被实例化,静态类里面的成员必须是静态的,或者常量 无法作为基类派生子类

抽象类abstact的类多层继承的时候是从上往下的继承链去创建的 抽象类中允许虚函数,虚函数和abstract是不能有函数体的

密封类sealed

到此为止,其他的类不能继承此类 是必须实现的类,不能声明为抽象类 密封类的成员函数不得声明为sealed 目的是防止重写接口或者类影像稳定性

大对象(大于85,000字节)会被放在专门的大对象堆中,它们的垃圾回收通常比较耗时。尽量避免创建过多的大对象。

打包的时候所有的dll都被打包进我的bin文件夹里面,我如果在其他的目录通过快捷方式运行我的exe实际上是先把workdir定位到我的程序目录然后运行,运行已编译的可执行文件时,它通常会在其所在的目录作为working dir相对路径寻找依赖项,包括开发时所依赖的 DLL 文件。对于 .NET Core 和 .NET 5+,这种行为是默认的。所以不要使用绝对路径引入自己的DLL

在 C# 中,where 关键字用于在泛型类型或泛型方法中对类型参数进行约束。通过 where 关键字,你可以指定泛型参数必须满足的条件,以确保代码的安全性和可靠性。

where 子句可以用于泛型类、泛型方法和泛型委托中,用来限制泛型参数的类型。以下是一些 where 子句的使用场景:

  1. 基类约束: where T : 基类名,指定泛型参数必须是指定基类或该基类的派生类。
  2. 接口约束: where T : 接口名,指定泛型参数必须实现指定接口。
  3. 构造函数约束: where T : new(),指定泛型参数必须有一个无参数的公共构造函数。
  4. 值类型约束: where T : 结构体,指定泛型参数必须是值类型。
  5. 引用类型约束: where T : class,指定泛型参数必须是引用类型。
  6. 特定类型约束: where T : 特定类型,可以是具体的类名、值类型、接口名等,用来限定泛型参数的类型。

委托多播的时候,类似于责任链,都会执行一遍,可以使用+-和-=给委托加上和减去 委托属于引用类型,引用类型默认为null,如果对委托多播进行了操作,记得判空!!

event智能

CLR在.NET中,静态构造函数或静态变量的初始化是在类第一次被引用时执行的,而不是在程序启动时。这种行为有时被称为“懒汉式”初始化,因为它推迟了对象的创建,直到真正需要时才创建。这种方式可以帮助减少程序启动时的开销,并且可以避免创建那些可能永远不会被使用的对象。

//泛型可以一次性规定多个
public class Pair<TFirst, TSecond>
{
    public TFirst First { get; set; }
    public TSecond Second { get; set; }

    public Pair(TFirst first, TSecond second)
    {
        First = first;
        Second = second;
    }
}

Pair<int, string> pair = new Pair<int, string>(10, "hello");
Console.WriteLine($"First: {pair.First}, Second: {pair.Second}");

泛型类也可以被继承,子类也可以扩展

抽象类可以声明构造函数和变量

引用类型是一定存储在堆上的,但是值类型要看所处的命名空间在哪里,参考结构体在类中声明的时候会跟随类被 分配到堆上

字符串判等,判空,拼接,包含,切片 C# 字符串(String) | 菜鸟教程open in new window

使用base访问的是父类的方法,this查找的是当前类的属性

在 C# 中,字符串拼接有几种不同的方法,每种方法都有其优缺点,具体取决于你的应用场景和性能需求。以下是几种常见的字符串拼接方式:

  1. 字符串运算符 +

    • 用法: 使用 + 运算符将多个字符串直接拼接。
    • 优点: 简洁易读,适合拼接少量的字符串。
    • 缺点: 当拼接大量字符串时,由于字符串的不可变性,每次拼接都会创建新的字符串对象,性能较差。
    string result = "Hello" + " " + "World!";
    
  2. string.Concat 方法

    • 用法: 调用 string.Concat 来拼接字符串。
    • 优点: 与 + 类似,但可以更高效地处理多参数拼接。
    • 缺点: 同样因为不可变性,拼接大量字符串时性能较差。
    string result = string.Concat("Hello", " ", "World!");
    
  3. string.Format 方法

    • 用法: 使用格式字符串和占位符进行拼接。
    • 优点: 更适合格式化拼接,能灵活插入不同类型的数据。
    • 缺点: 性能不如简单拼接,且在需要拼接大量字符串时开销较高。
    string name = "Alice";
    int age = 25;
    string result = string.Format("Name: {0}, Age: {1}", name, age);
    
  4. StringBuilder

    • 用法: 创建 StringBuilder 对象,通过 Append 等方法拼接字符串。
    • 优点: 适合需要频繁拼接和修改字符串的场景,性能高。
    • 缺点: 相较其他拼接方法,代码稍显复杂。
    var builder = new System.Text.StringBuilder();
    builder.Append("Hello");
    builder.Append(" ");
    builder.Append("World!");
    string result = builder.ToString();
    
  5. 插值字符串 ($"...")

    • 用法: 使用 $ 前缀的字符串,通过 {} 插入变量值。
    • 优点: 语法简洁,易于理解和维护。
    • 缺点: 与 string.Format 类似,在大量拼接或复杂计算的情况下性能较差。
    string name = "Alice";
    int age = 25;
    string result = $"Name: {name}, Age: {age}";
    

总结

  • 少量拼接:使用 + 运算符或 string.Concat
  • 格式化拼接:使用 string.Format 或字符串插值。
  • 大量拼接或循环拼接:使用 StringBuilder 以获得最佳性能。

如果在父类中定义了一个函数,那么子类可以直接调用这个函数而无需重新定义。这种情况下,子类直接继承了父类的功能。然而,虚函数和重写在面向对象编程中起到关键作用,尤其在实现多态性和动态方法绑定(dynamic binding)方面。

什么是虚函数

虚函数(virtual function)是一种允许子类在继承父类函数的同时,重新定义其行为的机制。在父类中定义虚函数,然后在子类中重写(override),可以让子类提供自己的实现。

为什么需要虚函数和重写

虚函数和重写的关键作用是实现多态性,即在运行时能够根据对象的实际类型调用相应的方法。这在设计灵活的面向对象系统时非常重要。

动态方法绑定

在没有虚函数的情况下,编译器在编译时决定调用哪个方法,这叫做静态方法绑定(static binding)。即使子类定义了一个与父类同名的方法,也不会发生多态行为,调用仍然是基于编译时的类型。

而有了虚函数,编译器会根据实际对象的类型在运行时决定调用哪个方法。这叫做动态方法绑定(dynamic binding)或后期绑定(late binding)。

示例

public class BaseClass
{
    public void RegularMethod()
    {
        Console.WriteLine("Base class method");
    }

    public virtual void VirtualMethod()
    {
        Console.WriteLine("Base class virtual method");
    }
}

public class DerivedClass : BaseClass
{
    public void RegularMethod()
    {
        Console.WriteLine("Derived class regular method");
    }

    public override void VirtualMethod()
    {
        Console.WriteLine("Derived class overridden virtual method");
    }
}

在这个示例中,RegularMethod在子类中重写,但不是虚函数。这意味着如果使用父类引用访问子类对象,调用的仍然是父类的RegularMethod

VirtualMethod是虚函数,子类通过override重写了这个方法。如果使用父类引用访问子类对象,调用的将是子类的VirtualMethod

测试虚函数行为

BaseClass baseClass = new BaseClass();
DerivedClass derivedClass = new DerivedClass();

BaseClass baseRefToDerived = new DerivedClass();

// 静态方法绑定,调用父类的 RegularMethod
baseRefToDerived.RegularMethod();  // 输出 "Base class method"

// 动态方法绑定,调用子类的 VirtualMethod
baseRefToDerived.VirtualMethod();  // 输出 "Derived class overridden virtual method"

在这个示例中,baseRefToDerived是父类类型的引用,但指向子类实例。如果没有虚函数和重写,调用RegularMethodVirtualMethod会根据引用类型,而不是实际对象的类型。这会导致行为与预期不符。

结论

虚函数和重写在实现多态性方面至关重要。它们使得在运行时根据对象的实际类型调用正确的方法成为可能,而不依赖于编译时的静态绑定。通过使用虚函数,面向对象编程可以更灵活、更易于扩展。

直接打印对象的时候打印出来的是什么

在 C# 中,直接打印对象时,会调用对象的 ToString() 方法来获取对象的字符串表示形式,然后将其输出到控制台。

如果你没有为特定类型的对象提供自定义的 ToString() 方法,那么将会使用默认的实现。默认情况下,ToString() 方法返回对象的类型的完全限定名。

例如,如果你有一个名为 MyClass 的类,如果你直接打印一个 MyClass 类的实例,它将会输出类的完全限定名,如下所示:

using System;

class MyClass
{
    // 类的成员
}

class Program
{
    static void Main()
    {
        MyClass obj = new MyClass();
        Console.WriteLine(obj); // 输出:命名空间.MyClass
    }
}

如果你想要自定义对象的字符串表示形式,你可以重写 ToString() 方法,并在其中返回你希望打印出的字符串:

using System;

class MyClass
{
    public override string ToString()
    {
        return "This is MyClass";
    }
}

class Program
{
    static void Main()
    {
        MyClass obj = new MyClass();
        Console.WriteLine(obj); // 输出:This is MyClass
    }
}

通过重写 ToString() 方法,你可以为对象提供更有意义的字符串表示形式,以便于调试和输出。

游戏的暂存

【不是特别重要,进阶可以稍微看一看《CLR Via C#》 4.1:所有类型都从 System.Object 派生-哔哩哔哩】《CLR Via C#》 12.1.5:泛型参数的逆变和协变_哔哩哔哩_bilibiliopen in new window
unity最完美的检测脚底板是否贴地代码_哔哩哔哩_bilibiliopen in new window Mono内存问题 【可以找找某些经验 为了做游戏,我花了10年自学程序、美术、音乐、策划-哔哩哔哩】 https://b23.tv/IIyzwjoopen in new window 这个人水平应该还行 unity引擎程序员职业规划_哔哩哔哩_bilibiliopen in new window

引擎的使用不仅要熟悉常用的组件和AIP, 输入系统,UI系统,动画系统,导航系统,资源管理,渲染系统这些最好也能掌握(这个是入行的技能要求了) 另外你现在是有项目实战能力的, 可以查漏补缺一下常用的系统模块(背包,商店,道具,装备,小地图,任务,NPC, 战斗可以等等,时间充裕的话也可以一并开发)

Loading...