将来需要学习的:

1.Span<T>;Memory<T>

2.C#中的Tread(线程) 与  Task(任务) async/await

2022.08.31

1.CultureInfo 类:

提供有关特定区域性(对于非托管代码开发,则称为“区域设置”)的信息。 这些信息包括区域性的名称、书写系统、使用的日历、字符串的排序顺序以及对日期和数字的格式化设置。【参考网站】(https://docs.microsoft.com/zh-cn/dotnet/api/system.globalization.cultureinfo?view=net-6.0)

2.断言(Assert):

Debug.Assert(条件表达式);当表达式为假时,会打印错误并终止程序。只在程序的Debug模式下有作用。作用:用断言明确表现某一段代码需要对程序状态做出某种假设。在交流角度,断言可以帮助程序阅读者理解代码所做的假设;在调试的角度,断言可以在距离bug最近的地方抓住它们。
【参考网站】1.C#中的断言(Assert) - itjeff - 博客园 (cnblogs.com) 2. 断言(assert)的用法 | 菜鸟教程 (runoob.com)

3.游戏中向量的点乘与叉乘的基本应用表现:

点乘:
(一个向量在另一个向量上的投影。)
1.判断两个向量是否垂直;点乘值为零时则垂直。(abcosθ,向量夹角θ为90度,则垂直)。2.计算目标是否在范围之内。3.判断目标在自身的前方后方:Vector3.Dot(transform.forward, target.position)。返回值为正时,目标在自己的前方(两个向量方向相同);返回值为负时,在自己的后方(两个向量方向相反);返回值为0时,在自己的正左方或者正右方。
叉乘:
1.判断两个向量是平行还是相交,返回值为0向量时为平行;方向可能完全相同也可能完全相反。2.求法线(叉乘的几何意义:absinθ)3.判断目标在自身的左右方位Vector3.Cross(transform.forward, target.position).y,求叉乘并取y轴向量。如果y向量为正时,目标在自己的右方;为负时,在自己的左方;返回值为0时,在自己的正前方或者正后方(左手坐标系)。
【参考网站】http://https://blog.csdn.net/qiaoquan3/article/details/70194685

2022.09.02

1.C#匿名类型:

提供了一种方便的方法,可以将一组只读属性封装到单个对象中,而无需首先显示定义一个类型。类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。
var v = new { Amount = 108, Message = "Hello" };
匿名类型通常用在查询表达式LINQ【https://docs.microsoft.com/zh-cn/dotnet/csharp/linq/】的select子句中
var products = new[] {new {Color = "红色", Price = 1, Task = 1.1}, new {Color = "绿色", Price = 3, Task = 0.1}};//匿名类型的数组
var productQuery = from prod in products
           select new {prod.Color, prod.Price};//在products选择符合条件的组成新的匿名类型productQuery 并使用var进行变量类型的声明

foreach (var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

//打印结果:
//Color=红色, Price=1
//Color=绿色, Price=3
【参考网站】匿名类型 | Microsoft Docs

2.C#7.0特性:元组(Tuples)与值元组(ValueTuple)

元组,可以实现多个返回值。 元组(Tuple)在 .Net 4.0 的时候就有了,但元组也有些缺点,如:
1)Tuple 会影响代码的可读性,因为它的属性名都是:Item1,Item2.. 。
2)Tuple 还不够轻量级,因为它是引用类型(Class)。
备注:上述所指 Tuple 还不够轻量级,是从某种意义上来说的或者是一种假设,即假设分配操作非常的多。
值元组(ValueTuple)解决了上述两个缺点:
1)ValueTuple 支持语义上的字段命名。
2)ValueTuple 是值类型(Struct)可以直接使用“=”来进行赋值。
3)同时满足以下两个条件时,两个元组可比较:
两个元组具有相同数量的元素。 例如,如果 t1 和 t2 具有不同数目的元素,t1 != t2 则不会进行编译。
对于每个元组位置,可以使用 == 和 != 运算符对左右侧元组操作数中的相应元素进行比较。 例如,(1, (2, 3)) == ((1, 2), 3) 不会进行编译,因为 1 不可与 (1, 2) 比较。
  1. System.ValueTuple 类型是值类型。 System.Tuple 类型是引用类型。
    System.ValueTuple 类型是可变的。 System.Tuple 类型是不可变的。
    System.ValueTuple 类型的数据成员是字段。 System.Tuple 类型的数据成员是属性。
  2. 用法:1.作为方法的返回值(当需要多个返回值时),此时不需要定义Out参数
    2.作为Out参数
    例:
    //1.作返回值
    var (minimum, maximum) = FindMinMax( new[] { 4, 7, 9 });
    Console.WriteLine($"Limits of [{string.Join(" ", ys)}] are {minimum} and {maximum}");
    
    (int min, int max) FindMinMax(int[] input)
    {
        //方法体
    }
    
    //2.作Out参数
    var limitsLookup = new Dictionary<int, (int Min, int Max)>()
    {
        [2] = (4, 10),
        [4] = (10, 20),
        [6] = (0, 23)
    };
    
    if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits))
    {
        Console.WriteLine($"Found limits: min is {limits.Min}, max is {limits.Max}");
    }
    // Output:
    // Found limits: min is 10, max is 20
    
    var tuple = (1, 2);                   // 使用语法糖创建元组
    var tuple2 = ValueTuple.Create(1, 2);       // 使用静态方法【Create】创建元组
    var tuple3 = new ValueTuple<int, int>(1, 2);  // 使用 new 运算符创建元组
    //var:(int,int)
    
    WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三种方式都是等价的。");
     
    (int a, float b) _valueTuple = (1, 1.2f);//创建值元组并赋变量名
    Console.WriteLine($"通过自定义变量名:Item1:{_valueTuple.a};Item2:{_valueTuple.b}"); 
    
    var _valueTuple1 = ValueTuple.Create(1, 2);
    (int a, int b) _valueTuple2 = _valueTuple1;
    Console.WriteLine($"值元组赋值:.Item1:{_valueTuple2.a};Item2:{_valueTuple2.b}");// _valueTuple2与_valueTuple2 public (int Y,intX)Coordinate {get;set;}//定义一个有两个返回值(int Y, int X)的Coordinate属性
    
    public List<(int a, string str)> list;  
    list.Add((1,"as"));
    
【参考网站】 C# 元组和值元组 - 北田 - 博客园 (cnblogs.com) 元组类型 - C# 参考 | Microsoft Learn

2022.09.09

1.垃圾回收器GC

GC释放内存不是立即释放,而是先放到f-reachable队列中,在合适的事件调用终结器,这样会延迟终结器的调用。调用了终结器后的对象才会回收其内存,如果希望对象能更快被释放,可以实现IDispose接口并调用。
GC.SuppressFinalize()的正确用法:
System.GC.SuppressFinalize(this(需要释放的对象)),调用这个方法后对象不会被放进f-reachable队列中,但是注意不放进队列就不会调用终结器,所以在Dispose中要完成终结器的功能。注意Dispose是需要自己显式调用或者放在using语句的括号中。

2.枚举,标志枚举

Enum 枚举分为简单枚举和标志枚举。简单枚举包含的值不用于组合,也不用于按位比较。标志枚举可以使用位操作符进行组合

标记枚举需要注意两点 1.指明 FlagsAttribute [Flags];2.枚举中的值应该是2的幂。
[Flags]
public enum FileProperty
{
    Hidden = 1,
    ReadOnly = 2,
    Encrypt = 4,
}

设置文件属性时,就可以利用OR自由组合:FileProperty fileP = FileProperty.Hidden | FileProperty.ReadOnly;
从这可以看出为什么标志的值要按2的幂排列,也可以看出标志多于32个时不能使用int类型的原因。相当于使用一个数组,属性的排列顺序与数组下标一一对应,当某个下标处的值为0时,表示对应的属性没有在组合中。
File类的GetAttributes()返回的就是一个枚举值的组合,如何确定组合中包含某个属性,可以使用&(and)操作符
FileAttributes attributes = File.GetAttributes(fileName);//获取特性
attributes & FileAttributes.Hidden == FileAttributes.Hidden; // 结果为true时表示存在这个属性

2022.09.15

1.有关图集打包

对于不规则的图形打包图集时不要勾选Tight Packing(紧凑打包),尽量调整大的Padding。否则打包后会显示不正确;但在编辑器中是正确的。因为在编辑器中读取的是单个精灵的纹理,而不是在图集中切割获取精灵纹理。
正确显示:
不正确显示:

2.Dictionary C#字典中  Add方法,TryAdd方法,与字典索引器的区别:

Add方法:如果添加的Key在字典中已经存在,会引发异常:
public static Dictionary<int, string> actDict = new();
static void Main(string[] args)
{
    actDict.Add(3, "123");
    actDict.Add(3, "abc");
    Console.WriteLine(actDict[3]);
}
//异常:System.ArgumentException:“An item with the same key has already been added. Key: 3” 提示已经存在相同的Key值
TryAdd方法:有Key不会覆盖Value没有则新加Key-Value
public static Dictionary<int, string> actDict = new();
static void Main(string[] args)
{
    actDict.TryAdd(2, "123");
    actDict.TryAdd(2, "abc");
    Console.WriteLine(actDict[2]);
}
//输出结果:123 

字典索引器: 有Key会覆盖Value没有则新加Key-Value
public static Dictionary<int, string> actDict = new();
static void Main(string[] args)
{
    actDict[1]="123";
    actDict[1]="abc";
    Console.WriteLine(actDict[1]);
}
//输出结果:abc

2022.09.16

1.try{} catch{}

/*
try{
程序代码块;
}
catch(Exception e){
异常处理代码块;
}
finally{
无论是否发生异常,均要执行的代码块;
}
*/
//thorw后的语句不会执行。
var i = 0;
try
{
// int a;
  // string b;
  // Console.WriteLine("请输入一个整数");
  // b = Console.ReadLine();
  // a = int.Parse(b);
  throw new Exception();//在当前代码块中throw后的语句不会执行
  i += 1;
  Console.WriteLine("1:" + i);
}
catch (Exception e)
{
  // Console.WriteLine(e);
  // throw;//throw后的语句不会执行
  i += 1;
  Console.WriteLine("2:" + i);
}
finally
{
  i += 2;
  // Console.WriteLine("finally块内总是能运行到");
  Console.WriteLine("3:" + i);
}
i += 3;
Console.WriteLine("4:" + i);

2.List中Contains方法 :

该方法默认比较器来确定相等性;比较器由集合中的类型的 IEquatable<T>.Equals方法定义。
例子:
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<Cube> cubes = new List<Cube>();

        cubes.Add(new Cube(8, 8, 4));
        cubes.Add(new Cube(8, 4, 8));
        cubes.Add(new Cube(8, 6, 4));

        if (cubes.Contains(new Cube(8, 6, 4))) {
            Console.WriteLine("An equal cube is already in the collection.");
        }
        else {
            Console.WriteLine("Cube can be added.");
        }

        //Outputs "An equal cube is already in the collection."
    }
}

public class Cube : IEquatable<Cube>
{

    public Cube(int h, int l, int w)
    {
        this.Height = h;
        this.Length = l;
        this.Width = w;
    }
    public int Height { get; set; }
    public int Length { get; set; }
    public int Width { get; set; }

    public bool Equals(Cube other)//定义具体的比较方法,满足条件时表示两个Cube相等。
    {
        if (this.Height == other.Height && this.Length == other.Length
            && this.Width == other.Width) {
            return true;
        }
        else {
            return false;
        }
    }
}


2022.09.22

1.text超采样:

首先将Horizontal Overflow 与 Vertical Overflow 设置为Overflow; text的字体大小和游戏对象的缩放成一定比例。例:设置其文本大小为80,游戏对象缩放为0.5,其实际显示的文本大小为则40,
对比图:

2.TextMeshPro—Text:



3.Unity性能优化:

合批:合并模型网格。减少材质的数量(共用一个材质)。打图集。
静态批处理:对场景中不移动的物体在属性面板中设置static。
GPU实例化,适用于动态对象。
**不能在静态批处理的同时使用GPU实例化

2022.09.26

1.对于unity中SVG格式的图片不正确显示的问题,是因为mate的损坏造成的。修复:选中资源,右键选择Reimport可以重新生成mate文件。


2022.10.08

1. 关于字符串的反转:

List<T>.Reverse();

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        List<string> dinosaurs = new List<string>();

        dinosaurs.Add("0.Pachycephalosaurus");
        dinosaurs.Add("1.Parasauralophus");
        dinosaurs.Add("2.Mamenchisaurus");
        dinosaurs.Add("3.Amargasaurus");
        dinosaurs.Add("4.Coelophysis");
        dinosaurs.Add("5.Oviraptor");

        Console.WriteLine();
        foreach(string dinosaur in dinosaurs)
        {
            Console.WriteLine(dinosaur);
        }

        dinosaurs.Reverse();

        Console.WriteLine();
        foreach(string dinosaur in dinosaurs)
        {
            Console.WriteLine(dinosaur);
        }

        dinosaurs.Reverse(1, 4);//反转从索引1到索引4之间的元素

        Console.WriteLine();
        foreach(string dinosaur in dinosaurs)
        {
            Console.WriteLine(dinosaur);
        }
    }
}

/* This code example produces the following output:

0.Pachycephalosaurus
1.Parasauralophus
2.Mamenchisaurus
3.Amargasaurus
4.Coelophysis
5.Oviraptor

5.Oviraptor
4.Coelophysis
3.Amargasaurus
2.Mamenchisaurus
1.Parasauralophus
0.Pachycephalosaurus

5.Oviraptor
1.Parasauralophus
2.Mamenchisaurus
3.Amargasaurus
4.Coelophysis
0.Pachycephalosaurus
 */

2.C# Find()和First()与FirstOrDefault()

1. Find方法只能在List<T>上使用,而后者能更广泛应用在IEnemerable<T>上。
Find最终是建立在Array的查找之上,而在IEnemerable上的FirstOrDefault是使用foreach查找的。因此,Find速度会比FirstOrDefault快很多,据测试可能会快一倍以上。
2. First:取序列中满足条件的第一个元素,如果没有元素满足条件,则抛出异常
3. FirstOrDefault:取序列中满足条件的第一个元素,如果没有元素满足条件,则返回默认值(对于可以为null的对象,默认值为null,对于不能为null的对象,如int,默认值为0)
First、FirstOrDefault的区别在于:当没有元素满足条件时,一个抛出异常,一个返回默认值。
因此,在使用时,一定要注意这个区别:
1、当确信序列中一定有满足条件的元素时,使用First方法,取到元素后,无需判断是否为null
2、当序列中可能找不到满足条件的元素时,使用FirstOrDefault方法,然后,一定要对返回值是否为null,进行不同的处理

2022.10.13

1.Compile,Build,Execute的区别

编写源代码(edit)-> 编译(compile)-> 连接,也叫生成(build)-> 运行(execute)
"compile"是“编译”的意思,“build”是“链接”“生成”的意思。

compile 的作用是对代码进行语法检查,将你的文本程序语言转化成计算机可以运行的“01010....”形式的二进制文件。
build 的作用是将你在程序中调用到的类库融合到你的程序中,比如你用到了printf()函数,那么内部实现该函数的类库代码就会添加到你的程序中。

compile:生成“.obj”文件或".o"文件,这个和编译器有关,vc++中是“.obj”文件。
build:在汇编里称link,在C里叫build,它的作用是生成可执行的exe文件。由于一个程序的源码可由多个文件组成。这些文件在第二步中分别编译,生成各自的目标文件(*.obj),
这一步的作用便是将这些obj文件,以及程序中需要的其它库文件(dll除开),统一到一个文件中来,形成单个的exe文件。此exe文件便可以在操作系统下直接运行了。

2.ToString的重写

class Person
{
   public string name;
   public int Height { get; set; }

   public override string ToString()
   {
        return "Person: " + name + " " + Height;
    }
}
Person p = new Person { name = "jump", Height = 180 };
Console.WriteLine(p); // 等价于Console.WriteLine(p.ToString());

//打印:Person: jump 180

2022.10.18

1.Transform.SetParent

public void SetParent (Transform p);
public void SetParent (Transform parent, bool worldPositionStays);
参数解释:
parent:父物体的Transform
worldPositionStays:如果为 true,则修改相对于父级的位置、缩放和旋转(更改局部坐标),使对象保持与之前相同的世界空间位置、旋转和缩放(世界坐标不变)。默认值为true

这个方法是把一个对象找个父级,如果worldPositionStays为true,那么该对象会保持世界坐标不变;如果为false那么该对象会保持局部坐标不变。
具体显示:
下图显示处于其原始位置的子 GameObject:
下面是在 worldPositionStays 设置为 true 的情况下调用 SetParent 之后的外观:
下面是在 worldPositionStays 设置为 false 的情况下调用 SetParent 之后的外观:

2.Unity中的世界坐标与局部坐标与转换

2022.10.20

1.C#中的var

· 通过var关键字声明的变量,变量类型是在编译期确定的,而不是在运行时确定
· 对于LINQ表达式返回的匿名类型,可以用var关键字声明的变量接收
例:
class Program
{
    static void Main(string[] args)
    {
        string[] strs = { "hello", "world", "nice", "to", "meet", "you" };
        var o = from s in strs
                where s.Length > 3
                select new { Key = s.Length, Value = s };//匿名类型
        foreach (var item in o)
        {
            Console.WriteLine(item.Key);
        }
    }
}
· 对于一些名称比较长的类,类实例化的时候,可以用var关键字声明的变量接收
var只能出现在局部变量声明或脚本代码中

2.弃元 :_

" _ ":弃元,相当于未赋值的变量,没有值,本质上是一个变量占位符,通过将下划线 (_) 赋给一个变量作为其变量名,指示该变量为一个占位符变量。
在switch的模式匹配中弃元可以匹配所以类型包括null

3.switch模式匹配

int a;
int b = 2;
a = b switch//switch的模式匹配
{
    2 => 3,
    _ => 1,
    //" _ ":弃元,相当于未赋值的变量,本质上是一个变量占位符,在switch的模式匹配中弃元可以匹配所以类型包括null
};
Console.WriteLine(a);// 3

2022.11.1

1.unity Mask组件

遮罩: 所有子物体只能在Mask的范围内才能显示,超出Mask范围的部分不显示。Mask的范围由挂载到同一个物体的Image组件确定。
            是一种修改控件子元素外观的方法,Mask将子元素限制为父级元素的形状,当子级比父级大时,子级只显示父级所包含的内容。
Mask只有一个属性:Show Mask Graphic——是否显示image的图像
【参考网站】遮罩 (Mask) - Unity 手册

2.unity Text富文本

标签 描述 示例 注意事项
标签 描述 实例 注意事项
b 以粗体显示文本。 We are <b>not</b> amused.
i 以斜体显示文本。 We are <i>usually</i> not amused.
size 根据参数值设置文本的大小(以像素为单位)。 We are <size=50>largely</size> unaffected. 尽管此标签可用于 Debug.Log,但如果大小设置得太大,您会发现窗口栏和控制台中的行间距看起来很奇怪。

color
根据参数值设置文本的颜色。可使用传统的 HTML 格式指定颜色。#rrggbbaa …其中的字母对应于十六进制数字对,表示颜色的红、绿、蓝和 Alpha(透明度)值。例如,完全不透明的青色将指定为 color=#00ffffff…
可通过大写或小写形式指定十六进制值;#FF0000 等效于 #ff0000。
We are <color=#ff0000ff>colorfully</color> amused 另一种选择是使用颜色的名称。这种方法更容易理解,但当然,颜色范围受限,并始终假设颜色完全不透明。<color=cyan>some text</color>
material 这仅对文本网格有用,使用参数指定的材质渲染文本部分。值为 Inspector 显示的文本网格材质数组的索引。 We are <material=2>texturally</material> amused
quad 这仅对文本网格有用,渲染与文本内联的图像。采用指定图像材质的参数、图像高度参数(以像素为单位)以及另外四个表示待显示图像的矩形区域的参数。与其他标签不同,quad 不会包裹一段文本,因此没有结束标签;斜杠字符放在初始标签的末尾,表示“自动关闭”。 <quad material=1 size=20 x=0.1 y=0.1 width=0.5 height=0.5> 这将选择渲染器材质数组中位置的材质,并将图像的高度设置为 20 像素。图像的矩形区域由 x、y、宽度和高度值决定,这些值全部表示为纹理的未缩放宽度和高度的一定比例。
【参考网站】富文本 - Unity 手册

3.参数数组


使用 params 关键字可以指定采用数目可变的参数的方法参数。 参数类型必须是一维数组。如果 params 参数的声明类型不是一维数组,则会发生编译器错误 CS0225。
在方法声明中的 params 关键字之后不允许有任何其他参数,并且在方法声明中只允许有一个 params 关键字。
使用 params 参数调用方法时,可以传入:
  • 数组元素类型的参数的逗号分隔列表。
  • 指定类型的参数的数组。
  • 无参数。 如果未发送任何参数,则 params 列表的长度为零。
需要注意:1.类型转换出现问题;2.数据元素可以是一个数组
List<int> tempList = new List<int>();  tempList.Add(1);
SaveRecord(tempList);//此时参数数组的第一个元素是一个List集合。

public void SaveRecord(params object[] param)
{
   if (param == null)
   {
      _CurTrackingQuestIDList = null;
      return;
   }
   _CurTrackingQuestIDList = new List<int>();
   _CurTrackingQuestIDList = ((List<int>)param[0]);
}

2022.11.07

1.String的赋值操作

在C#中String是引用类型,内容存放在堆中,地址存放在栈中。
但是当我们把一个字符串变量赋给另一个字符串时,就会创建一个全新的String对象,就是说这个时候就会有两个对象。并不是指向同一引用地址。
public static void Main()
{
    string s1 = "原始字符串";
    string s2 = s1; //注意此时会创建一个新对象
    Console.WriteLine("s1 is " + s1);
    Console.WriteLine("s2 is " + s2);
    s1 = "改变字符串";
    Console.WriteLine("s1 is now " + s1);
    Console.WriteLine("s2 is now " + s2);
}
//打印:
s1 is 原始字符串
s2 is 原始字符串
s1 is now 改变字符串
s2 is now 原始字符串 

2022.11.15

1.使用DoTween动画做渐隐

public void PlayDoTween(TweenCallback backCall)
{
    Tween _viewItemDoTween = DOTween.To
    (
        () => _viewItemCG.alpha,//getter 获取要变化的值
        x => _viewItemCG.alpha = x,//setter 为要变化的值赋值
        0,//endValue 结束值
        1//duration 渐变周期
    );

    if (backCall != null)
    {
      _viewItemDoTween.OnComplete(backCall);//完成后的回调函数
    }
}

2.事件触发器EventTrigger

  1. 获取EventTrigger组件    
    EventTrigger trigger = _btnSelf.gameObject.GetComponent<EventTrigger>();
  2. 实例化一个EventTrigger.Entry对象
    EventTrigger.Entry entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerEnter, callback = new EventTrigger.TriggerEvent() };
    //有两个成员 1.EventTriggerType(事件触发器的类型)2.回调函数
    
  3. 为EventTrigger.Entry对象添加回调函数
  4. entry.callback.AddListener(OnMouseEnter);
  5. 在EventTrigger中的triggers集合中添加EventTrigger.Entry对象
    trigger.triggers.Add(entry);
    当指针进入时调用回调方法。
挂载EventTrigger的游戏对象会对所有事件类型进行拦截。比如滚动窗口的滚动事件,当窗口中的游戏对象使用EventTrigger进行事件注册时,当发生窗口滚动操作时EventTrigger中的滚动事件会相应而不会执行滚动窗口的滚动事件。

3.C#属性事件

public delegate void MyHandler();
private static event MyHandler myEvent;
public static event MyHandler MyEvent
{
    add { myEvent += value; }
    remove { myEvent -= value; }
}

//---事件的注册与注销-----
// --- 当使用以下方式进行事件的注册与注销时会导致注销不成功;因为两个delegate 是两个方法 其引用地址不同-----------
MyEvent += delegate { Console.WriteLine(123); };
MyEvent -= delegate { Console.WriteLine(123); };
//------------------

// --以下方法可以正确的进行事件的注册和注销  因为用的同一个方法 方法地址相同 --
MyEvent += MyEventAction();
MyEvent -= MyEventAction();
private static void MyEventAction()
{ Console.WriteLine(123); }
//事件触发: myEvent.Invoke();

2022.12.2

1.使用unity AVPro Video插件播放透明视频

前提条件:视频需要有alpha通道,如AVI格式
将Media Player组件中的Platform Specific==》Windows==》Preferred Video ApI选项改为Direct Show

2022.12.15

1.枚举的比较函数Equals()会导致GC

枚举类型属性值类型,Equals函数是系统函数,它的参数是object是引用类型,所以在枚举类型转化为引用类型时,系统会使用装箱操作产生GC。枚举类型做比较可以将枚举改为int 直接使用 == 作比较。
在Update内的代码尽量避免去做导致拆装箱,临时对象创建的代码。

 2022.12.22

1.C#中的类型转换

类型装换分为隐式类型转换与显式类型转换,
 隐式转型:系统默认的,不需要加以声明就可以进行的转换,在隐式转换过程中,编译器无需对转换进行详细检查就能够安全的执行转换,一般不会失败,转换过程中也不会导致信息丢失;装箱就是一种隐式类型装换

隐式数值转换

隐式数值转换包括以下几种:
●从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类型。
其中,从int,uint,或long到float以及从long到double的转换可能会导致精度下降,但决不会引起数量上的丢失

隐式枚举转换

隐式枚举转换允许把十进制整数0转换成任何枚举类型,对应其它的整数则不存在这种隐式转换。

隐式引用转换

隐式引用转换包括以下几类:
●从任何引用类型到对象类型(Object)的转换(装箱)。
●从类类型s到类类型t的转换,其中s是t的派生类。
●从类类型s到接口类型t的转换,其中类s实现了接口t。
●从接口类型s到接口类型t的转换,其中t是s的父接口。
从元素类型为Ts的数组类型S向元素类型为Tt的数组类型T转换,这种转换需要满足下列条件:
●S和T只有元素的数据类型不同,但它们的维数相同。
●Ts和Tt都是引用类型。
●存在从Ts到Tt的隐式引用转换。

●从任何数组类型到System.Array的转换。
●从任何代表类型到System.Delegate的转换。
●从任何数据类型或代表类型到System.ICLoneable的转换。
●从空类型(null)到任何引用类型的转换。


 显式转型:有可能引发异常、精确度丢失及其他问题的转换方式。需要明确指定的转换类型。拆箱是一种显示类型转换

显式数值转换

显式数值转换是指当不存在相应的隐式转换时,从一种数字类型到另一种数字类型的转换。包括:
●从sbyte到byte,ushort,uint,ulong,或char。
●从byte到sbyte或char。
●从short到sbyte,byte,ushort,uint,ulong,或char。
●从ushort到sbyte,byte,short,或char。
●从int到sbyte,byte,short,ushort,uint,ulong,或char。
●从uint到sbyte,byte,short,ushort,int,或char。
●从long到sbyte,byte,short,ushort,int,uint,ulong,或char。
●从ulong到sbyte,byte,short,ushort,int,uint,long,或char。
●从char到sbyte,byte,或short。
●从float到sbyte,byte,short,ushort,int,uint,long,ulong,char,或decimal。
●从double到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,或decimal。
●从decimal到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,或double。
这种类型转换有可能丢失信息或导致异常抛出,转换按照下列规则进行:

●对于从一种整型到另一种整型的转换,编译器将针对转换进行溢出检测,如果没有发生溢出,转换成功,否则抛出一个OverflowException异常。这种检测还与编译器中是否设定了checked选项有关。
●对于从float,double,或decimal到整型的转换,源变量的值通过舍入到最接近的整型值作为转换的结果。如果这个整型值超出了目标类型的值域,则将抛出一个OverflowException异常。
●对于从double到float的转换,double值通过舍入取最接近的float值。如果这个值太小,结果将变成正0或负0;如果这个值太大,将变成正无穷或负无穷。如果原double值是Nan,则转换结果也是NaN。
●对于从float或double到decimal的转换,源值将转换成小数形式并通过舍入取到小数点后28位(如果有必要的话)。如果源值太小,则结果为0;如果太大以致不能用小数表示,或是无穷和NaN,则将抛出InvalidCastException异常。
●对于从decimal到float或double的转换,小数的值通过舍入取最接近的值。这种转换可能会丢失精度,但不会引起异常。

显式枚举转换

●从sbye,byte,short,ushort,int,uint,long,ulong,char,float,double,或decimal到任何枚举类型。
●从任何枚举类型到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,或decimal。
●从任何枚举类型到任何其它枚举类型。

显式枚举转换是这样进行的:它实际上是枚举类型的元素类型与相应类型之间的隐式或显式转换。比如,有一个元素类型为int的枚举类型E,
则当执行从E到byte的显式枚举转换时,实际上做的是从int到byte的显式数字转换;当执行从byte到E的显式枚举转换时,实际上是执行byte到int的隐式数字转换。

显式引用转换

显式引用转换包括:
●从对象类型(Object)到任何引用类型
●从类类型S到类类型T,其中S是T的基类。
●从基类型S到接口类型T,其中S不是密封类,而且没有实现T。
●从接口类型S到类类型T,其中T不是密封类,而且没有实现S。
●从接口类型S到接口类型T,其中S不是T的子接口。
从元素类型为Ts的数组类型S到元素类型为Tt的数组类型T的转换,这种转换需要满足下列条件:
●S和T只有元素的数据类型不同,而维数相同。
●Ts和Tt都是引用类型。
●存在从Ts到Tt的显式引用转换。

●从System.Array到数组类型。
●从System.Delegate到代表类型。
●从System.ICloneable到数组类型或代表类型。
显式引用转换发生在引用类型之间,需要在运行时检测以确保正确。

2.is与as的区别

is:检查一个对象是否兼容于其他指定的类型,并返回一个bool值,如果一个对象是某个类型或是其父类型的话就返回为true,否则的话就会返回为false。永远不会抛出异常

如果对象引用为null,那么is操作符总是返回为false,因为没有对象可以检查其类型。
as:检查一个对象是否兼容于其他指定的类型,并返回结果,如果不兼容则返回null,否则返回目标结果。用as来进行类型转换的时候,所要转换的对象类型必须是目标类型或者转换目标类型的派生类型
区别:
1、as在转换的同时兼判断兼容性,如果无法进行转换,则 as 返回 null(没有产生新的对象)而不是引发异常。有了as就不要再用try-catch来做类型转换的判断了。因此as转换成功要判断是否为null。
2、as是引用类型类型的转换或者装箱转换,不能用与值类型的转换。如果是值类型只能结合is来强制转换。
3、is只是做类型兼容判断,并不执行真正的类型转换。返回true或false,不会返回null,对象为null会返回false。
4、as模式的效率要比is模式的高,因为借助is进行类型转换的化,需要执行两次类型兼容检查。而as只需要做一次类型兼容,一次null检查,null检查要比类型兼容检查快。

2022.12.23

1.C#嵌套类

嵌套类:把一个类的定义放到另一个类或结构中。例:
class OuterClass
{
    class NestedClass
    {
            //嵌套类
    }
}
C# 中的嵌套类不支持在方法级别进行定义,也不能是匿名类型,更无法像使用当前类一样使用外部类的成员。在 C# 的嵌套类内想要使用外部类的成员,必须将外部类的实例通过构造函数传递进去。
class OuterClass 
{
     string s;
     private NestedClass nestedClass;

       private void GetNestedClass()
       {
            nestedClass = new NestedClass(this);
            nestedClass.GetOuterString();
       }

     class NestedClass
     {
          private OuterClass _outer;
          
          public NestedClass(OuterClass o) { _outer = o;}
          public string GetOuterString() { return _outer.s; } 
          // 可以像访问 NestedClass 中的成员一样直接访问,不需要任何对 OuterClass 的引用
     }
  
}

2023.1.6

1.销毁DoTween动画

2023.2.1

1.transform.LookAt()与Quaternion.LookRotation()

transform.LookAt():使向前向量指向目标的当前位置;
Quaternion.LookRotation():以指定的向前和向上方向创建一个旋转;
******************************************************

2.Horizontal Layout Group 与Vertical Layout Group 中的Use Child Scale属性

布局组件是否考虑子UI元素的缩放(Width与Height由子UI元素的RectTransform的Scale.X与Scale.Y控制)。

3.ScreenPointToLocalPointInRectangle:将屏幕坐标转换到RectTransform的本地空间中的矩形平面上的位置

rect要在其中查找点的 RectTransform。
cam:与屏幕空间位置关联的摄像机。对于设置为 Screen Space - Overlay 模式的 Canvas 中的 RectTransform,cam 参数应为 null。
screenPoint:屏幕空间位置。
localPoint:矩形变换本地空间中的点。

2023.2.17

1.String.Format() : 数字的格式化输出

float a1 = 123.45f;
string aStr = string.Format("{0:0%}", a1);
string aStr1 = $"{a1:0%}";
Console.WriteLine(aStr);//12345%
Console.WriteLine(aStr1);//12345%,‘:0%’取百分比,无小数点
(5条消息) C#:String.Format数字格式化输出_CHCH998的博客-CSDN博客_c# string 格式化输出

2.readonly 关键字

readonly 指示只能在声明期间或在同一个类的构造函数中向字段赋值。 可以在字段声明和构造函数中多次分配和重新分配只读字段。

2023.2.24

1.C#事件只能在声明类中被调用


2.Sprite中texture属性

2023.3.15

1.DOTweenAnimation的回调函数

OnStart:在补间第一次启动时,在任何延迟之后调用。
OnPlay:每次补间状态从暂停变为播放状态(包括补间第一次开始播放),在任何延迟之后调用。
OnUpdate:补间动画播放时的每一帧都调用。
OnStep:在每个循环结束时调用的事件。
OnComplete:补间动画结束时的回调。
OnRewind:当补间被倒放到起始位置时调用。可以通过倒放直到结束,也可以手动倒放。
OnCreated:在补间动画实例化后立即调用。

2.C#中char转换int 

1.Char.GetNumericValue();//将数字的Unicode字符转换为其等效的数字。
using System;
 
public class Example
{
    public static void Main()
    {
        char ch = '9';
        int intVal = (int)Char.GetNumericValue(ch);
 
        Console.WriteLine(intVal);
    }
}
 
/*
    输出: 9
*/


2.CharUnicodeInfo.GetDecimaDigitValue();//返回指定数字的Unicode字符的十进制数字值。
using System;
using System.Globalization;
 
public class Example
{
    public static void Main()
    {
        char ch = '9';
        int intVal = CharUnicodeInfo.GetDecimalDigitValue(ch);
 
        Console.WriteLine(intVal);
    }
}
 
/*
    输出: 9
*/

3.Int32.Parse()/Int32.TryParse();
using System;
 
public class Example
{
    public static void Main()
    {
        char ch = '9';
        int intVal = int.Parse(ch.ToString());
 
        Console.WriteLine(intVal);
    }
}
 
/*
    输出: 9
*/

4.与‘0’相减 获取从0到9的数字
using System;
 
public class Example
{
    public static void Main()
    {
        char ch = '9';
        int intVal = ch - '0';
 
        Console.WriteLine(intVal);
    }
}
 
/*
    输出: 9
*/


2023.3.14

1.创建委托的一个方法:Delegate.CreateDelegate(委托类型,方法)


2.is运算符


3.List中的Where与Select


4.将两个数组合并为一个数组 CopyTo


5.ToggleGroup中AllowSwitchOff属性


6.Image中的useSpriteMesh 如果为真会生成一个紧密的网格


2023.3.17

1.使用Unity Profiler 找出耗性能的代码

UnityEngine.Profiling.Profiler.BeginSample("。。。。。。");
// 耗时代码 ....balabala
UnityEngine.Profiling.Profiler.EndSample();
BeginSample 和 EndSample之间不能有return关键字。

2023.3.25

1.ref结构

在C#中,ref关键字用于传递参数的引用,以便函数可以修改调用方传递的变量。它可以用于引用任何可变类型,包括结构。当使用ref关键字传递结构作为参数时,将传递结构的引用而不是复制该结构的副本。
例如,考虑以下结构:
public struct Person
{
    public string Name;
    public int Age;
}
现在,如果我们编写一个函数来修改一个Person对象的年龄,我们可以使用ref关键字来传递该对象的引用:
public void UpdateAge(ref Person person, int newAge)
{
    person.Age = newAge;
}
然后,我们可以这样调用该函数:
Person john = new Person { Name = "John", Age = 30 };
UpdateAge(ref john, 35);
注意,调用UpdateAge函数时,我们需要使用ref关键字来传递john的引用。这样,UpdateAge函数将能够修改john的年龄属性。如果不使用ref关键字,将只会传递john的副本,而不是引用,因此UpdateAge函数将无法修改john的属性。

2.使用范围(Range)和索引(Index)来访问集合类型(字符串,数组)的子集

给定一个字符串或数组 c,可以使用 c[index..i] 语法来创建一个范围,它表示从 index 到 i-1 的元素子集。
string str = "Hello, World!";
string subStr = str[2..7]; // "llo, "
例如,如果 c 是一个字符串,我们可以使用以下代码来获取从第 2 个字符到第 6 个字符的子字符串:
需要注意的是,范围的上限是开区间,也就是不包含终点元素。如果 i 超出了 c 的长度,将会抛出一个 System.IndexOutOfRangeException 异常。

3.IEnumerable<T>接口

IEnumerable<T> 是 C# 中定义的一个接口,用于表示一个泛型集合,其中包含一个或多个类型为 T 的元素。这个接口可以用于定义可枚举的集合,即可以通过循环迭代器枚举集合中的元素。
IEnumerable<T> 接口定义了一个方法 GetEnumerator(),该方法返回一个实现了 IEnumerator<T> 接口的对象,用于遍历集合中的元素。
IEnumerator<T> 接口定义了三个方法:
    MoveNext():将迭代器移到集合的下一个元素,如果成功移动到下一个元素则返回 true,否则返回 false。
    Reset():将迭代器重置为集合的第一个元素之前。
    Current:获取当前位置的元素。
在 C# 中,可以使用 foreach 循环语句来遍历实现了 IEnumerable<T> 接口的集合。例如,假设我们有一个 List<int> 集合,我们可以使用以下代码来遍历它:
List<int> list = new List<int> { 1, 2, 3 };
foreach (int item in list)
{
    Console.WriteLine(item);
}
需要注意的是,实现 IEnumerable<T> 接口的集合类型必须同时实现 IEnumerator<T> 接口,或者提供一个实现了 GetEnumerator() 方法的迭代器,以便 foreach 循环语句可以正确地遍历集合中的元素。

4.C#中的yield return 与Unity协程中的yield return

1.C#:

yield return 是 C# 中的一个关键字,用于在迭代器中返回一个值,并将迭代器的当前位置移到下一个值。通过使用 yield return,我们可以轻松地创建实现 IEnumerable<T> 接口的集合类型,而无需显式地实现 IEnumerator<T> 接口的方法。
当我们在迭代器中使用 yield return 语句时,C# 编译器会自动生成一个实现了 IEnumerator<T> 接口的类,该类包含一个实例变量来存储迭代器的状态信息,并实现了 MoveNext() 和 Current 方法,以便在迭代器中遍历集合中的元素。

下面是一个简单的示例,使用 yield return 创建一个返回从 0 到 n 的整数的集合:
public static IEnumerable<int> GetIntegers(int n)
{
    for(int i = 0;i <= n;i++)
    {
        yield return i;
    }
}
在这个例子中,GetIntegers 方法返回一个实现了 IEnumerable<int> 接口的集合类型,它使用 yield return 语句在循环中返回每个整数,并在每次迭代时将迭代器移到下一个整数。我们可以使用以下代码遍历 GetIntegers 方法返回的集合:
foreach(int i in GetInteger(5))
{
    Console.Write(i + ",");
}//输出结果:0,1,2,3,4,5,
需要注意的是,使用 yield return 创建集合类型的一个重要好处是,它允许我们按需生成元素,而不需要一次性将它们全部存储到内存中。这对于处理大型数据集或需要高效处理集合的场景非常有用。

2.Unity:

Unity 中的协程通过使用 yield return 语句来实现暂停和继续执行。在 Unity 中,协程是一种特殊的函数类型,可以让你控制函数在执行过程中暂停,然后再在指定的时间或条件满足时继续执行。
当我们在协程函数中使用 yield return 语句时,它会暂停当前的协程,并等待指定的时间或条件满足后继续执行。Unity 中的协程可以使用以下方式来定义:
IEnumerator MyCoroutine()
{
    // 第一段代码
    yield return new WaitForSeconds(1.0f);

    // 第二段代码
    yield return new WaitUntil(() => someConditionIsTrue);

    // 第三段代码
    yield return null;

    // 第四段代码
    yield break;
}
在这个例子中,MyCoroutine 方法定义了一个协程,包含四个 yield return 语句。第一条语句使用 WaitForSeconds 类型暂停协程一秒钟。第二条语句使用 WaitUntil 类型暂停协程,直到指定的条件为真。第三条语句使用 null 暂停协程,表示需要等待下一帧。最后一条语句使用 yield break 结束协程。
需要注意的是,Unity 中的协程必须使用 StartCoroutine 方法来启动。例如,如果我们想要启动上面的协程,可以使用以下代码:
StartCoroutine(MyCoroutine());

5.Quaternion.FromToRotation


2023.3.30

1.List.Sort() 与 List.OrderBy()

使用指定或默认的IComparer<T>实现或提供Comparison<T>委托对List<T>中的全部元素或部分元素进行排序。
关于返回值 -1 与 1 的区别:返回-1:由大到小排序  返回1:由小到大排序
定义Foo类,继承IComparer<T>,与ICOmparable<T>
class Foo : IComparer<Foo>, IComparable<Foo> //自定义类使用Sort排序方法需要继承IComparable<>
{
    public int     Id;
    public string? Name;

    public int Compare(Foo? foo1, Foo? foo2)
    {
        if (foo2 != null && foo1 != null && foo1.Id > foo2.Id)
        {
            return 1; //foo1>foo2
        }
        else if (foo2 != null && foo1 != null && foo1.Id < foo2.Id)
        {
            return -1; //foo1<foo2
        }
        else
        {
            //如果相同根据名字排序
            if (foo1?.Name?.CompareTo(foo2?.Name) > 0) //<====>(String.Compare(foo1?.Name, foo2?.Name, StringComparison.Ordinal) > 0)
            {
                return 1; //foo1>foo2
            }
            else
            {
                return -1; //foo1<foo2
            }
        }
    }

    /// <summary> 这个例子中与Compare排序相反 </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public int CompareTo(Foo? other)
    {
        if (other != null && other.Id > Id)
        {
            return 1; //other>this
        }
        else if (other != null && other.Id < Id)
        {
            return -1; //other<this
        }
        else
        {
            //如果相同根据名字排序
            if (other?.Name?.CompareTo(Name) > 0) //<====>(String.Compare(other?Name, Name, StringComparison.Ordinal) > 0)
            {
                return 1; //other>this
            }
            else
            {
                return -1; //other<this
            }
        }
    }
}
定义Foo类的集合
List<Foo> foos = new List<Foo>
{
    new Foo { Id = 1, Name = "b" },
    new Foo { Id = 5, Name = "c" },
    new Foo { Id = 7, Name = "A" },
    new Foo { Id = 4, Name = "B" },
    new Foo { Id = 1, Name = "v" },
    new Foo { Id = 9, Name = "F" },
    new Foo { Id = 6, Name = "f" },
    new Foo { Id = 3, Name = "B" }
};
使用 List.Sort()进行比较 1.实现IComparer接口 2.实现IComparable接口 3.使用Comparison委托
#region Sort

Console.WriteLine("\n------- 使用Sort进行排序 Start-------\n");

List<int> ints = new List<int>{ 1,5,7,4,1,9,6,3,};

Console.WriteLine("\n------- Int排序 -------\n");

ints.Sort();

ints.ForEach
(
    i =>
    {
        Console.WriteLine($"使用Name值排序 Id:{i}");
    }
);

Console.WriteLine("\n------- 继承IComparer<Foo> 正序 -------\n");

IComparer<Foo> fooComparer = new Foo();
foos.Sort(fooComparer);

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"继承IComparer<Foo> Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 继承IComparable<Foo> 倒序 -------\n");

foos.Sort();

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"继承IComparable<Foo> Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 使用Comparison委托 -------\n");

foos.Sort
(
    delegate(Foo foo1, Foo foo2)
    {
        if (foo1.Id > foo2.Id)
        {
            return 1; //foo1>foo2
        }
        else if (foo1.Id < foo2.Id)
        {
            return -1; //foo1<foo2
        }
        else
        {
            //如果相同根据名字排序
            if (foo1.Name?.CompareTo(foo2.Name) > 0) //<====>(String.Compare(foo1.Name, foo2.Name, StringComparison.Ordinal) > 0)
            {
                return 1; //foo1>foo2
            }
            else
            {
                return -1; //foo1<foo2
            }
        }
    }
);

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"使用Comparison委托 Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 使用Sort进行排序 End-------\n");

#endregion
打印的结果:
------- 使用Sort进行排序 Start-------


------- Int排序 -------

使用Name值排序 Id:1
使用Name值排序 Id:1
使用Name值排序 Id:3
使用Name值排序 Id:4
使用Name值排序 Id:5
使用Name值排序 Id:6
使用Name值排序 Id:7
使用Name值排序 Id:9

------- 继承IComparer<Foo> 正序 -------

继承IComparer<Foo> Id:1,Name:b
继承IComparer<Foo> Id:1,Name:v
继承IComparer<Foo> Id:3,Name:B
继承IComparer<Foo> Id:4,Name:B
继承IComparer<Foo> Id:5,Name:c
继承IComparer<Foo> Id:6,Name:f
继承IComparer<Foo> Id:7,Name:A
继承IComparer<Foo> Id:9,Name:F

------- 继承IComparable<Foo> 倒序 -------

继承IComparable<Foo> Id:9,Name:F
继承IComparable<Foo> Id:7,Name:A
继承IComparable<Foo> Id:6,Name:f
继承IComparable<Foo> Id:5,Name:c
继承IComparable<Foo> Id:4,Name:B
继承IComparable<Foo> Id:3,Name:B
继承IComparable<Foo> Id:1,Name:v
继承IComparable<Foo> Id:1,Name:b

------- 使用Comparison委托 -------

使用Comparison委托 Id:1,Name:b
使用Comparison委托 Id:1,Name:v
使用Comparison委托 Id:3,Name:B
使用Comparison委托 Id:4,Name:B
使用Comparison委托 Id:5,Name:c
使用Comparison委托 Id:6,Name:f
使用Comparison委托 Id:7,Name:A
使用Comparison委托 Id:9,Name:F

------- 使用Sort进行排序 End-------
使用List.OderBy()
#region OrderBy

Console.WriteLine("\n------- 使用OrderBy进行排序 Start-------\n");
int[] orderArr =  { 9, 8, 7, 6, 5, 4, 1, 2, 3 };

foos = foos.OrderBy
(
    e =>
    {
        var index = 0;
        index = Array.IndexOf(orderArr, e.Id); //查找orederArr数组中 与e.Id相同的索引值。 没有找到返回-1

        if (index != -1) { return index; }
        else
        {
            return int.MaxValue;
        }
    }
).ToList();

Console.WriteLine("\n------- 使用orderArr中的值排序 -------\n");

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 使用Name值排序 -------\n");

foos = foos.OrderBy(foo => foo.Name).ToList();

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"使用Name值排序 Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 使用ID值排序 -------\n");

foos = foos.OrderBy(foo => foo.Id).ToList();

foos.ForEach
(
    p =>
    {
        Console.WriteLine($"使用Id值排序 Id:{p.Id},Name:{p.Name}");
    }
);

Console.WriteLine("\n------- 使用OrderBy进行排序 End-------\n");

#endregion
结果:
/*------- 使用OrderBy进行排序 Start-------


------- 使用orderArr中的值排序 -------

Id:9,Name:F
Id:7,Name:A
Id:6,Name:f
Id:5,Name:c
Id:4,Name:B
Id:1,Name:b
Id:1,Name:v
Id:3,Name:B

------- 使用Name值排序 -------

使用Name值排序 Id:7,Name:A
使用Name值排序 Id:1,Name:b
使用Name值排序 Id:4,Name:B
使用Name值排序 Id:3,Name:B
使用Name值排序 Id:5,Name:c
使用Name值排序 Id:6,Name:f
使用Name值排序 Id:9,Name:F
使用Name值排序 Id:1,Name:v

------- 使用ID值排序 -------

使用Id值排序 Id:1,Name:b
使用Id值排序 Id:1,Name:v
使用Id值排序 Id:3,Name:B
使用Id值排序 Id:4,Name:B
使用Id值排序 Id:5,Name:c
使用Id值排序 Id:6,Name:f
使用Id值排序 Id:7,Name:A
使用Id值排序 Id:9,Name:F

------- 使用OrderBy进行排序 End-------

2.Array.IndexOf()


3.C#中的结构体Struct

1.Struct是值类型,在做复制操作时,会创建一个新的副本,而不是引用原结构体。
2.尽管结构体是值类型,但在 C# 中也可以使用 new 关键字来初始化结构体。(new 关键字在 C# 中既可以用于引用类型(Reference Type),也可以用于值类型(Value Type))。
使用 new 关键字初始化结构体时,会在堆上为其分配内存,并返回指向该结构体的引用。
3.结构体也可以定义无参构造函数。与类(Class)类似,结构体可以有多个构造函数,包括无参构造函数。如果没有定义任何构造函数,那么编译器会为结构体生成一个默认的无参构造函数。如果定义了其他构造函数,编译器将不会为其生成默认构造函数。
在结构体的构造函数中,可以使用 this 关键字来访问结构体的成员变量。需要注意的是,如果结构体的构造函数中没有为所有成员变量赋值,则未赋值的成员变量将会被初始化为其类型的默认值。

4.String类型尽量不要使用 == null 或 ==“”进行空判断 要使用string.IsNullOrEmpty

5.特性(Attribute)  自定义特性: XXXXAttribute

自定义特性:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ToggleTabOneEventAttribute: Attribute
{
    public int Index { get; set; }

    public ToggleTabOneEventAttribute(int index)
    {
        Index = index;
    }
}
1.所有自定义属性都必须继承System.Attribute 
2.自定义属性的类名称必须为 XXXXAttribute 即是已Attribute结尾

了解 AttributeUsage
    AttributeUsage 有三个 属性 ,分别是 
         public bool AllowMultiple { get; set; }   作用:是否能在一个目标身上多次使用。
         public bool Inherited { get; set; } 作用 :特性是否能继承到子类身上。
         public AttributeTargets ValidOn { get; } 作用:设置特性的可使用范围。
 AttributeTargets
public enum AttributeTargets
{
    Assembly = 1,//     可以对程序集应用特性。
    Module = 2,//     可以对模块应用特性。
    Class = 4,//     可以对类应用特性。
    Struct = 8,//     可以对结构应用特性,即值类型。
    Enum = 16,//     可以对枚举应用特性。
    Constructor = 32,//     可以对构造函数应用特性。
    Method = 64,//     可以对方法应用特性。
    Property = 128,//     可以对属性应用特性。
    Field = 256,//     可以对字段应用特性。
    Event = 512,//     可以对事件应用特性。
    Interface = 1024,//     可以对接口应用特性。
    Parameter = 2048,//     可以对参数应用特性。
    Delegate = 4096,//     可以对委托应用特性。
    ReturnValue = 8192,//     可以对返回值应用特性。
    GenericParameter = 16384,//     可以对泛型参数应用特性。
    All = 32767,//     可以对任何应用程序元素应用特性。
}

6.检测某个类(方法,属性等)是否使用了指定的特性

1.Attribute中静态方法 IsDefined

如果有指定的Attribute的实例与目标关联,则返回true。
Attribute.IsDefined(目标的方法, typeof(指定的特性类型));

2.Attribute中静态方法 GetCustomAttributes 

返回一个数组,其中每个元素都是应用于目标的指定attribute类的一个实例。该方法通常用于已将AllowMultiple设为true的attribute,或者用于列出已应用的所有attribute。

3.Attribute中静态方法 GetCustomAttribute

返回应用于目标的指定attribute类的一个实例。该方法通常用于已将AllowMultiple设置为false的attribute。

4.MethodInfo中的实例方法 GetCustomAttribute

MethodInfo实例.GetCustomAttribute<特性>()

C#中检测某个类(方法、程序集等各种部分)是否应用了指定的特性以及对特性的一些简单操作 - wangyafei_it - 博客园 (cnblogs.com)

7.System.Reflection.BindingFlags

System.Reflection.BindingFlags是一个枚举类型,用于指定在使用反射时搜索类型成员的方式。如果您想搜索一个类型的所有公共静态成员,您可以使用BindingFlags.Public | BindingFlags.Static。
以下是每个BindingFlags选项的具体含义:

Public:表示搜索公共成员,即public修饰的成员,包括继承的成员。
NonPublic:表示搜索非公共成员,即private、protected、internal修饰的成员,但不包括继承的成员。
Instance:表示搜索实例成员,即非静态成员。
Static:表示搜索静态成员,即由static修饰的成员。
FlattenHierarchy:表示在搜索成员时搜索整个类型层次结构,包括继承的成员。
DeclaredOnly:表示仅搜索在当前类型中声明的成员,而不搜索基类型或实现接口中的成员。
IgnoreCase:表示在搜索成员时不区分大小写。
InvokeMethod:表示搜索方法。
GetField:表示搜索字段。
SetField:表示搜索可写字段。
SetProperty:表示搜索可写属性。
PutDispProperty:表示搜索COM对象的属性。
PutRefDispProperty:表示搜索COM对象的ref属性。
ExactBinding:表示使用精确的匹配来查找成员。
SuppressChangeType:表示取消默认行为,即如果搜索方法的参数类型与提供的参数类型不匹配,则将尝试将参数类型转换为搜索方法的参数类型。如果设置了此标志,则不会尝试转换参数类型。
OptionalParamBinding:表示允许忽略参数的可选参数。

System.Reflection.BindingFlags.Default并不是一个有效的BindingFlags值,它并不存在。当您使用这个值作为参数调用反射方法时,会抛出System.ArgumentException异常。

如果您需要使用默认绑定标志来执行反射操作,可以简单地将BindingFlags.Default替换为BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic。
这个组合的标志将允许您在类型的实例成员和公共/非公共成员之间进行查找,这与默认的绑定行为相同。


使用BindingFlags.DeclaredOnly标志获取指定类型中定义的方法的示例代码:
Type type = typeof(MyClass);
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);//获取MyClass类中符合条件的方法
foreach (MethodInfo method in methods)
{
    Console.WriteLine(method.Name);
}

2023.4.23

1.unity有关应用程序的退出:

1.MonoBehaviour.OnApplicationQuit()

在应用程序退出前,发送给所有的游戏对象。在Editor中,当用户停止播放模式时,将调用该函数。MonoBehaviour是一个基类,所有Unity脚本都继承自MonoBehaviour
Note:1.iOS 应用程序通常会被暂停而不退出。 您应该在 iOS 构建的“Player Settings”中勾选“Exit on Suspend”,以使游戏退出而不是暂停, 否则您可能收不到该调用。如果未勾选“Exit on Suspend”, 您会收到 OnApplicationPause 调用。

2.Application.wantsToQuit

当播放器应用程序想要退出时,Unity会调用这个事件。当事件被调用时,已经开始退出的过程也可以被取消。这意味着玩家不一定会退出。对于有保证的退出事件:Application.quitting事件。
返回为True:退出过程继续;返回为False:退出过程会取消。
Note:在编辑器中退出游戏模式时,将忽略此事件的返回值。 重要信息:返回值在 iPhone 上不起作用。在 iPhone 操作系统下,应用程序无法阻止终止。
exe文件点击关闭按钮时调用这个方***出现问题,点击exe关闭按钮虽然会正常执行这个方法,但是也会执行OnApplicationQuit()方法。

3.Application.quitting

当播放器应用程序正在退出并且无法取消时,Unity会调用这个事件。
Note:如果玩家被迫退出或发生崩溃,这将不会触发。当无法取消退出过程时会调用此事件
Note:iOS 应用程序通常会暂停并且不会退出。您应该在 iOS 版本的播放器设置中勾选“暂停时退出”,以使游戏退出而不是暂停,否则您可能看不到此调用。如果未勾选“暂停时退出”,那么您将看到对 OnApplicationPause 的调用。

2.RuntimeInitializeOnLoadMethod 特性

允许一个运行时类在没有用户操作的情况下在游戏运行时被初始化。

2023.5.11

1.更改预制件的编辑环境


2.对于 IDragHandler,  IEndDragHandler,  IPointerClickHandler,  ISubmitHandler等接口的实现需要脚本继承于MonoBehaviour

3.ToggleGroup的EnsureValidState()方法

确保ToggleGroup仍然是有效的状态,在ToggleGroup下的Toggle的Ison均为false时,会将第一个Toggle的Ison设置为true;仅在ToggleGroup启动时或一个Toggle从Group中删除时有效

4.InputField中onValidataInput事件


2023.6.2

1.精灵九宫格切图的作用

2023.6.8

1.Image组件 Preserve Aspect:


2.图集中的 Allow Rotation 与 Tight Packing 选项

2023.6.16

1.为元组赋默认值的报错与修改


2.Object类型转值元组


转换为值类型不可以直接使用as关键字。

2023.8.29

1.Unity3d获取ParticleSystem里面的材质球的方法

ParticleSystem system = go.GetComponent<ParticleSystem>();
Renderer renderer= system.GetComponent<Renderer>();
Material mat = renderer.material;

2.Transform.SetSiblingIndex()

1.SiblingIndex 从0开始依次向后增加1

2.设置 SiblingIndex 时,如果设置的索引>当前索引 ,原有索引上的元素至当前元素之下的元素一律向上移动(索引-1),就是把下面的通通上移使得目标索引位置空出,然后放入自己。

3.设置 SiblingIndex 时,如果设置的索引<当前索引,原有索引上的元素至当前元素之上的元素一律向下移动(索引+1),就是把上面的通通下移使得目标索引位置空出,然后放入自己。

4.设置 SiblingIndex 时,大于ChildCount-1 等于 ChildCount-1,小于0 无效

5.新加元素,索引一律为 ChildCount-1, 也就是最后一个

2023.11.02

1.Unity Mask(遮罩)组件实现方式

使用GPU的模板缓冲区来实现遮罩。第一个遮罩元素将 “1”写入模板缓冲区。遮罩下面的所有元素在渲染的时候进行检查,仅渲染到模板缓冲区中有 1 的区域。 *嵌套掩码会将增量位掩码写入缓冲区,这意味着可渲染子级需要具有要渲染的模板值和逻辑。

2使用RectMask2D实现遮罩效果

3.Button(按钮)相关

按钮用于在用户单击再松开时启动某项操作。如果在松开单击之前将鼠标移开按钮控件,则不会执行操作。

4.获取Unity中游戏元素的自身索引的方法

2023.11.28

1.创建材质预设(Create Material Preset)

为某一游戏对象在已有材质的前提下创建一个与已有材质有属性不同的单独材质时 可以使用Create Material Preset(右键点击已有材质后出现包含此选项的选项框)

2.TextMeshPro 创建对应字体集或图集需要“选中”对应的字体和图

2023.12.27

1.使用富文本表现文本透明度


2.Visual Studio 的cspro项目文件

结构:

<Project Sdk="Microsoft.NET.Sdk">
<!-- 属性组 -->
  <PropertyGroup>
    <OutputType>输出类型</OutputType>
    <TargetFramework>netcoreapp5.0</TargetFramework>
    <RootNamespace>根命名控件</RootNamespace>
    <LangVersion>8</LangVersion>
    <AssemblyName>程序集名字</AssemblyName>
  </PropertyGroup>  <!-- Debug时的输出路径 -->
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>TRACE2_0;SERVER;NOT_UNITY,_MONITOR</DefineConstants>
    <OutputPath>..\..\Bin\</OutputPath>
    <TreatWarningsAsErrors>False</TreatWarningsAsErrors>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
<!-- Release时的输出路径-->
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DefineConstants>TRACE2_0;SERVER;NOT_UNITY</DefineConstants>
    <OutputPath>..\..\Bin\</OutputPath>
    <TreatWarningsAsErrors>False</TreatWarningsAsErrors>
  </PropertyGroup>

  <ItemGroup>
        <!--项目引用-->
    <ProjectReference Include="..\Hotfix\Server.Hotfix.csproj" />
    <ProjectReference Include="..\Model\Server.Model.csproj" />
  </ItemGroup>

  <ItemGroup>
    <None Update="NLog.config">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

  <ItemGroup>
    <Folder Include="Properties" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="Triggers">
      <HintPath>..\..\Libs\Triggers.dll</HintPath>
    </Reference>
    <Reference Include="UnityEngine">
      <HintPath>..\..\Libs\UnityEngine.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup>
<!--Nuget包引用-->
    <PackageReference Include="MiniProfiler.AspNetCore" Version="4.2.22" />
  </ItemGroup>
<!-- Import 文件引用-->
    <Import Project ="XXXXX.props"/>

</Project>

引用props文件

csproj文件引用props文件,以及props文件引用props文件,通过Import进行引用

<Import Project ="XXXXX.props"/>

2024.1.10

1.使用DoFade将周期设置为0设置透明度与直接设置透明度的区别:


2.选择一个可选对象的方法

Select();

2024.3.4

1.Toggle Group : Allow Switch Off

是否允许没有Toggle处于打开状态。
如果启用了此设置,按当前打开的开关将关闭它,因此没有打开开关。如果此设置被禁用,按下当前打开的开关将不会改变其状态。
即使allowSwitchOff为false,如果只有在加载场景或实例化组时才切换组中的开关,则Toggle group不会立即执行其约束。它只会阻止用户关闭开关。

2.AssetDatabase.LoadAssetAtPath()

使用AssetDatabase加载资源时其路径的起始为Assets文件夹;

3.不能直接对结构体中的成员进行赋值操作


翻译报错:访问属性“colors”时返回临时值。当被访问的结构体不被分类为变量时,不能修改结构体成员
修改:
ColorBlock colorBlock = _btn.colors;
colorBlock.normalColor = IsOpen? UITools.GetColor("#FFFFFF") : UITools.GetColor("#5C5C5C");
_btn.colors = colorBlock;

4.Unity粒子特效的大小自适应

在Inspector面板中找到Particle System面板,然后找到 Scaling Mode,即缩放模式,将其调整为Hierachy,即跟随父节点缩放

选完之后你会发现你的粒子系统“不见了”,其实只是Unity帮你重置了该粒子特效的大小为0。只需手动将该特效大小调整为你想要的Size

2024.3.19

1.PlayerPrefs.GetInt


2.关于预制件中材质属性更改后Git不识别更改:需要手动进行保存。不能只靠预制件的自动保存

3.九宫格切图与自适应


4.UI自适应图片大小由文字大小控制



2024.3.28

1.Button点击后依旧高亮


2.DataTime.Ticks

获取表示此实例的日期和时间的刻度数。 表示此实例的日期和时间的刻度数。该值介于 DateTime.MinValue.Ticks 和 DateTime.MaxValue.Ticks 之间。
public long Ticks {get;}
一毫秒(ms)有 一万个Tick;一秒有一千万个Tick

2024.4.12

1.Unity导入图片类型为Sprite时,图片实际显示比原图发白更改一下参数


如果在图集中,需要对图集进行设置:


2024.4.19

1.DateTime.Now与DateTime.UtcNow的区别


在使用DateTime.Now做计时操作时服务器与客户端会出现明显的差异,使用DateTime.UtcNow则可以避免这一现象。

2.Unity3d中获取Particle System中的材质球

通过Particle System实例获取ParticleSystemRenderer组件,在ParticleSystemRenderer组件中获取想要的材质球
ParticleSystem particleSystem.GetComponent<ParticleSystemRenderer>().material

2024.5.11

1.in参数修饰符

方法声明中需要in修饰符但是在调用方法时不需要声明。
in 修饰符允许编译器为自变量创建一个临时变量,并传递对该自变量的只读引用;按引用传递而不是按值传递。

2.UGUI--Dropdown 下拉框


3.

所有集合都是可枚举的,是因为他们实现了IEnumerable<T>,不过理论上讲并不是所有可枚举的对象都是集合,除非实现了ICollection<T>接口,这说明它要实现Add(),Clear(),Contains(),CopyTo()和Remove()方法。同时ICollection<T>也扩展了IEnumerable<T>。需要得到一个序列只需要一个实现的IEnumerable<T>的对象。LINQ处理的是值或对象的序列,而不只是集合。

2024.5.30

1.Toggle设置Ison时需要注意的事项

2.PlayerPrefs.Save()

PlayerPrefs 是一个做持久化存储的类。它可以将字符串、浮点和整数值存储到用户的平台注册表中。Unity - Scripting API: PlayerPrefs (unity3d.com)
PlayerPrefs.Save();方法会保存所有的更改。会在 OnApplicationQuit() 期间自动保存。由于写入 PlayerPrefs 可能会导致卡顿,因此建议在游戏过程中不要调用此函数。

2023.6.27

1.Image组件的ImageType为Tiled时,对应的纹理Wrap Mode需要设置为Repeat

2.使用正则表达式匹配空白字符

使用 \s可以匹配任何空白字符,包括空格、制表符、换行符等,如果表示单纯的匹配空格的话可以使用:[ ]。方括号中的字符表示需要匹配的字符;方括号中有一个空格就是要匹配空格符。

3.Unity--射线检测设置检测层级

Physics.Raycast(Ray ray , out RaycastHit hitinfo , float maxDistance , int layerMask)
参数:layerMask用于设置可以被检测的层级
使用左移操作获取对应层级的掩码,
如:layerMask =1<<1 表示允许检测第一层。
当需要增加层级时可以使用  或者  + 来操作 如:layerMask += 1<<2 增加允许检测的第二层;layerMask |= 1<<3 增加允许检测的第三层;
可以使用取反操作去取除当前层级的其他层级 如:  layerMask = ~layerMask表示检测除1,2,3层级的其他层级。

4.单精度float与双精度double的有效数字位

单精度浮点数占4个字节,32bit,有效数字8位;单精度浮点数占8个字节,64bit,有效数字16位,
float的范围为-2^128 ~ +2^128(-3.40E+38 ~ +3.40E+38)
double的范围为-2^1024 ~ +2^1024(-1.79E+308 ~ +1.79E+308)

2024.8.29

1.C# 判断数据是否为NaN的方法

float.IsNaN() / double.IsNaN()
在C#的浮点数计算中,0除以0将得到NaN,正数除以0将得到PositiveInfinity,负数除以0将得到NegativeInfinity。
在Unity中可以使用这种方法避免将NaN值对Transform进行赋值而产生的 “Invalid AABB inAABB ”报错

2.ToggleGroup的默认选中

2024.11.15

1.在使用TextMeshPro时,遇到字体文件中没有的字体时会导致现有的字体换行


原因:TextMeshPro在使用了字体文件中不存在的文本时会将不存在的文本替换为TMP Setting中 “Missing Character Unicode”中所配置的文本默认为 0 (\u0020)及空字符。进而回导致换行操作。
具体警告信息:

解决方法:将Missing Character Unicode”中所配置的值改为8203(\u200b)及零宽度字符可以理解为不可见字符。