Csharp基本语法速通

ooowl
  • 游戏开发
  • dotnet
About 12 min

Csharp基本语法速通

也不知道该放哪

装箱比拆箱更耗性能,装箱需要在堆上分配内存 在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...