unity的语言问题记录(委托相关)

NGUI跟随鼠标实例位置

  // 获取鼠标位置
        Vector3 mousePos = Input.mousePosition;

        // 将屏幕坐标转换为NGUI的世界坐标
        Vector3 worldPos = UICamera.mainCamera.ScreenToWorldPoint(mousePos);
        worldPos.z = 0; // 确保图片在UI层

        // 设置图片位置
        instantiatedImage.transform.position = worldPos;

// 将屏幕坐标转换为NGUI的世界坐标 Vector3 worldPos = UICamera.mainCamera.ScreenToWorldPoint(mousePos); worldPos.z = 0; 

List

unity的语言问题记录(委托相关)_第1张图片

在处理List集合时,foreach循环和for循环配合list[i]的使用确实在大多数情况下是功能等效的,但它们在某些方面还是有一些区别和适用场景:

1. foreach循环

foreach (var item in myList)
{
    Console.WriteLine(item);
}
  • 可读性foreach循环通常更简洁和易读,特别适合需要遍历整个集合而不修改集合的场景。
  • 避免索引错误:由于不使用索引,foreach循环减少了由于错误使用索引而导致的异常。
  • 更安全foreach不允许在遍历过程中修改集合(如添加或删除元素),因此更安全,但这也意味着它不适用于需要修改集合的场景。

2. for循环

for (int i = 0; i < myList.Count; i++)
{
    Console.WriteLine(myList[i]);
}
  • 灵活性for循环允许更灵活的操作。你可以通过索引访问和修改集合中的元素,还可以轻松地跳过或重复某些元素。
  • 部分遍历:你可以通过修改索引变量的值,轻松实现部分遍历,或者从特定位置开始遍历集合。
  • 支持反向遍历:通过调整索引变量的初始值和步长,for循环可以轻松实现从集合末尾向前遍历。
  • 修改集合:在for循环中,可以安全地对集合进行添加、删除或替换操作,只需小心管理索引。

3. 性能

  • 对于List这种基于数组的数据结构来说,foreachfor在性能上几乎没有差别,因为foreach在内部使用了类似索引的方式来遍历集合。
  • 然而,在某些情况下,for循环可能略微优于foreach,尤其是当你频繁访问元素索引时,因为for循环可以避免foreach循环在每次迭代中创建的额外迭代器对象。

4. 适用场景

  • foreach:适用于遍历整个集合、不需要知道索引或者不打算修改集合的场景。
  • for:适用于需要部分遍历、按特定顺序遍历、反向遍历,或者需要修改集合内容的场景。

总结:

  • 如果你只是遍历集合并读取元素,foreachfor几乎没有区别,foreach更简洁。
  • 如果需要修改集合内容、实现复杂的遍历逻辑,或者部分遍历,for循环可能会更适合。

元组解构赋值

元组解构赋值,这种语法允许在一行代码中交换两个变量的值。这种语法在C# 7.0及以上的版本中引入,并且可以用于元组的解构和多变量的同时赋值。

例如,这段代码:

(keys[index1], keys[index2]) = (keys[index2], keys[index1]);

在这里,(keys[index1], keys[index2]) 是一个元组,包含两个元素。右边的 (keys[index2], keys[index1]) 也是一个元组。C# 的解构赋值语法允许你直接将右边元组的值分配给左边的对应位置的变量,这样你可以很方便地交换两个变量的值。

为什么用元组解构赋值?

  • 简洁:不需要像传统方法那样使用临时变量来交换两个值。
  • 直观:在同一行代码中直接交换多个变量的值,代码更加清晰。

传统的交换方式

在引入这个语法之前,交换两个变量通常是这样实现的:

var temp = keys[index1];
keys[index1] = keys[index2];
keys[index2] = temp;

元组解构赋值提供了一种更加简洁的方式来实现相同的操作。

这个语法虽然在C#中相对较新,但它在其他编程语言如Python中早已广泛使用。

C#中的这种新特性,同样有C# 7.0及以后版本引入的其他功能,例如本地函数、模式匹配等。

var

在C#中,var是一个隐式类型变量声明的关键字,它允许编译器根据右侧赋值的类型自动推断变量的类型。当你使用var来声明一个变量时,编译器会根据赋值的表达式确定该变量的实际类型。

举个例子,如果你将一个List赋值给一个var变量,编译器会自动推断出该变量的类型是List

var myList = new List { 1, 2, 3 };

在这个例子中:

  • var myList 声明了一个变量myList
  • 右边的赋值是 new List { 1, 2, 3 },这是一个 List 类型的对象。
  • 因此,编译器会将 myList 的类型推断为 List

这意味着 myList 是一个 List 类型的变量,并且你可以像操作普通 List 变量一样操作它。

关键点:

  • var 并不是一个动态类型编译时它的类型就已经确定了。在编译后,var 就会被替换为实际的类型。
  • var 只能用于局部变量声明,并且必须在声明时进行初始化,编译器才能推断类型。

因此,var 可以存储一个 List,因为编译器能够从赋值的表达式中推断出 List 的类型。

除了谓词还可以在什么情况下使用

在 C# 中,除了谓词(如 AnyWhere 等 LINQ 方法)之外,lambda 表达式 可以用于多种场景。以下是一些常见的应用场景:

1. 委托

Lambda 表达式 可以用来简化委托的使用。委托是一种引用方法的类型,在 C# 中可以使用 lambda 表达式 来创建匿名方法并将其赋值给委托。

示例:
Func add = (x, y) => x + y;
int result = add(3, 4); // result 为 7

 在这个例子中,add为声明的func委托名,=>后的部分直接等价于{return x+y;}

至于谓词在List.Any中的使用,因为谓词可以说是返回bool的一种委托,所以有异曲同工之妙

2. 事件处理

在事件处理程序中,lambda 表达式 可以用作处理事件的匿名方法。

示例:
button.Click += (sender, e) => 
{
    Console.WriteLine("Button clicked!");
};

这个例子中,当按钮被点击时,lambda 表达式 定义的匿名方法将被调用。

3. 线程与任务

在多线程编程中,可以使用 lambda 表达式 来简化线程或任务的定义。

示例:
Task.Run(() => 
{
    // 任务代码
    Console.WriteLine("Task running");
});

4. LINQ 查询表达式

除了常见的 AnyWhere 方法外,lambda 表达式 还可以用于 SelectOrderByGroupBy 等 LINQ 方法。

示例:
var names = items.Select(item => item.name).ToList();

在这个例子中,lambda 表达式 用于从 items 列表中选择 name 属性,并生成一个新列表。

5. 简化条件逻辑

你可以在任何需要传递函数或执行代码块的地方使用 lambda 表达式,比如简化条件逻辑。

示例:
Action action = () => Console.WriteLine("Hello, World!");
action();

这个例子中,Action 是一个不接受参数且不返回值的委托类型。lambda 表达式 定义了 action 的行为,并在后续调用时执行。

6. 自定义扩展方法

你可以使用 lambda 表达式 在自定义扩展方法中作为参数传递,来灵活地定义行为。

示例:
public static void ForEach(this IEnumerable source, Action action)
{
    foreach (T element in source)
    {
        action(element);
    }
}

// 使用扩展方法
items.ForEach(item => Console.WriteLine(item.name));

 委托中的闭包提供了强大的功能,使得你能够创建灵活的、具有状态的匿名函数

info => info.id == 1001

为什么要写这么奇怪的语法?

这是因为 lambda 表达式 的语法和功能所决定的。Lambda 表达式 是 C# 中用于简化匿名方法的一种语法,它可以直接在代码中定义一个小型函数,并将其传递给 LINQ 方法(如 AnyFirstOrDefaultWhere 等)。这些方法依赖于 lambda 表达式 来描述要对集合中的每个元素执行的操作。

为什么可以把 item 当参数来写?

Lambda 表达式 的作用就是为每个元素定义一个临时函数。这个临时函数接受集合中的一个元素作为输入参数,并对其进行操作。因此,当你写 item => item.id == 1001 时,item 就是这个临时函数的参数,表示集合中的每一个单独的元素。

为什么知道它就是个体?

这是 LINQ 方法的设计原理决定的。比如,Any 方法定义为接受一个返回布尔值的函数(也就是 Predicate,它会自动将集合中的每个元素依次传递给这个函数,并检查这个元素是否满足给定的条件。因此,在 Any 方法的上下文中,lambda 表达式 的参数必然就是集合的一个元素。

为什么要用这种语法?

Lambda 表达式 是为了简化代码而设计的。相比传统的匿名方法,它更加简洁,且与 LINQ 查询表达式紧密结合,使得对集合进行查询、过滤、投影等操作变得更加直观和方便。

传统匿名方法的写法:

lambda 表达式 之前,如果要实现相同的功能,可能会用如下的代码:

bool exists = items.Any(delegate(iteminfo item) {
    return item.id == 1001;
});

使用 lambda 表达式 后,代码简化为: 

bool exists = items.Any(item => item.id == 1001);

这个语法更简洁,更易读,也减少了代码的冗余。

Lambda 表达式的结构:

Lambda 表达式 的语法是 (参数) => { 表达式或代码块 },其中:

  • 参数 是传递给匿名函数的输入参数,在 LINQ 中通常是集合的单个元素。
  • =>lambda 运算符,表示从参数到函数体的映射。
  • { 表达式或代码块 } 是函数体,可以是单个表达式(返回值)或一个代码块。
var numbers = new List { 1, 2, 3, 4, 5 };

// 使用 Lambda 表达式查找偶数
bool hasEven = numbers.Any(n => n % 2 == 0);

// Lambda 表达式中的 `n` 是 List 的每个元素,类似于传统循环中的 `foreach (var n in numbers) { }`

在这个例子中,n => n % 2 == 0 是一个 lambda 表达式,它接受集合中的每一个 int 元素 n,并检查这个元素是否为偶数。

总结:

  • Lambda 表达式 是为了简化代码并与 LINQ 紧密结合的一种语法。
  • 表达式中的参数表示集合中的单个元素。
  • 通过这种语法,你可以简洁地定义对集合每个元素的操作,从而更方便地对集合进行过滤、查询等操作。

为什么不用填上参数类型

在 C# 的 lambda 表达式 中,可以不用显式指定参数类型的原因,源于编译器的类型推断机制。

类型推断的原理

LINQ 方法(如 AnyWhereSelect 等)的使用过程中,lambda 表达式 所操作的对象是某个集合中的元素,而这个集合的元素类型是明确的。因此,编译器能够自动推断出 lambda 表达式 参数的类型。

List items = new List();

// 使用 lambda 表达式查找 id 为 1001 的元素
bool exists = items.Any(item => item.id == 1001);

在这个例子中,items 是一个 List,编译器知道 List 中的元素类型是 iteminfo,所以在 item => item.id == 1001 中,item 的类型自动被推断为 iteminfo

类型推断的好处

  1. 代码简洁:你不必重复写类型信息,代码更加简洁、易读。
  2. 减少冗余:避免不必要的类型声明,可以使代码更加清晰,尤其在复杂的查询表达式中。

需要显式指定类型的情况

虽然通常编译器可以推断出参数类型,但在某些情况下,显式指定参数类型可能是必要的。例如:

  1. 当编译器无法推断类型时:如果类型推断不明确,编译器可能会要求你显式地指定类型。
  2. 使用复杂类型或匿名类型:有时候,显式声明类型可以让代码更清晰,尤其在涉及复杂的或不常见的类型时。

显式指定参数类型的方式

如果你愿意,也可以显式指定参数的类型。显式指定参数类型时,需要使用括号包裹参数:

bool exists = items.Any((iteminfo item) => item.id == 1001);
bool exists = items.Any((iteminfo item) => {return item.id == 1001;
});也可以

在这个例子中,iteminfoitem 的类型。这种写法与前面不显式指定类型的写法功能相同,只是显式地告诉编译器 item 的类型是 iteminfo

总结

  • 类型推断:编译器可以通过 lambda 表达式 的上下文自动推断参数的类型,因此你通常不需要显式指定类型。
  • 代码简化:类型推断机制让代码更简洁,减少了不必要的冗余。
  • 灵活性:在需要时,你仍然可以显式地指定参数类型,以提高代码的可读性或解决推断问题。

查找 List里是否存在某个值

1. 使用 Any 方法

Any 方法用于确定集合中是否存在满足指定条件的元素。如果存在则返回 true,否则返回 false

bool exists = items.Any(item => item.id == 1001);

 这里的item指的是List的items的单个个体

AnyFirstOrDefaultWhere 等方法中的 lambda 表达式中,item => item.id == 1001item 代表 List 中的每个单独的元素。

你可以给这个参数取任何名字,只要它能清晰地表达你的意图,并且在 lambda 表达式中使用一致即可。

2. 使用 FirstOrDefault 方法

FirstOrDefault 方法用于查找第一个满足条件的元素,如果不存在这样的元素,则返回 null 或类型的默认值。

iteminfo foundItem = items.FirstOrDefault(item => item.id == 1001);

你可以通过检查 foundItem 是否为 null 来判断是否找到了满足条件的元素。

3. 使用 Where 方法(如果需要获取所有匹配项

如果可能有多个 id1001 的项,并且你想获取所有这些项,可以使用 Where 方法。

List matchingItems = items.Where(item => item.id == 1001).ToList();

 

你可能感兴趣的:(unity,游戏引擎)