0%

该文章译自Unity官网博客,原文:Custom == operator, should we keep it?

TL;DR: Unity的==是魔改版,与原生C#里的行为不一样。所以不要对UnityEngine.Object派生出的任何东西(包括MonoBehaviour)使用C#的空值相关语法糖,包括?.????=等等(三元运算符可以用),而应该老老实实使用== null来判空。

正文

当你在Unity中进行这样的操作时:

1
2
3
4
if (myGameObject == null) 
{
//...
}

Unity对==运算符做了一些特殊处理。与大多数人所期望的不同,我们对==运算符有一个特殊实现。

这样做是为了达到两个目的:

  1. 当一个MonoBehaviour有字段时,仅仅在编辑器中1,我们不会将这些字段设置为“真null”,而是设置为一个“假null”对象。我们自定义的==操作符能够检查某物是否是这种假null,并采取相应的行为。虽然这种机制怪怪的,但它能让我们在假null中存储信息,这样当你对它调用一个方法,或者读取一个属性时,它可以给你更多的上下文信息。如果没有这个技巧,你只会得到一个NullReferenceException,一个堆栈跟踪,但你不知道到底哪个GameObjectMonoBehaviour的字段是空的。而有了这个技巧,我们就可以在检查器中突出显示GameObject,也可以给你更多的提示:“看起来你正在访问这个MonoBehaviour中的一个未初始化的字段,请用Inspector将这个字段指向某个东西”。
阅读全文 »

其实19年在实习期间写CFET的时候,就有用到过Attribute这个特性[1],但当时并没有研究过它的作用和原理,只是照着文档给的案例使用。最近准备看看Unity中是如何用Lua实现热更新的,发现attribute满天飞,这就不得不理解一下了。

简单看了看,attribute似乎和反射息息相关,正好反射这块我也不太了解,这次就将一些相关的概念一并记录吧。

特性 Attribute

特性是一种标签,可以用方括号贴在各种元素[2]之前,给这些元素添加元数据。元数据包括编译器指令、注释、描述、方法、类等信息。

许多特性都附带一些参数,如在声明element的时候,通过带参的attribute附加元数据,按下面的格式使用:

1
2
[SomeAttribute(positional_parameters, name_parameter = value, ...)]
element

特性实际上是System.Attribute的一系列派生类,它们的后缀均为"Attribute",在打标签的时候这个后缀可以省略掉。

预定义的Attribute

微软在.NET库里已经内置了一堆System.Attribute的派生类,我们可能只会接触到其中一小部分,这里举几种常用的为例。

ObsoleteAttribute

Obsolete用于指示过时、弃用的元素。这些元素被认为不应该使用,因此在有调用到这种元素的地方,会产生Warning,并且附带参数message所包含的提示信息。如果建议使用某种新方案,就可以写在提示信息里。

对过时元素告警

另外,还有可选的第二个参数iserror,如果被设置为true,则会产生Error而不是Warning,阻止编译。

阅读全文 »

注:以下文稿写于2020年,虽未完整,为了不卡杂记的序号,还是发出来了。
日后会在这里添加更多内容,也可能另起一篇拾遗(

这一篇是真正的“杂”记。这段时间在刷LeetCode的同时,阅览了一些Java中常见的问题,鉴于Java与C#的殊途同归,也有了些收获。

为查漏补缺,将之前理解还不够明了的部分记在这里,主要是涉及OOP思想的一些概念。

关于多态

面向对象设计有四大特征:

  • 抽象:通过类和接口的属性与方法实现
  • 继承:通过类和接口的继承实现
  • 封装:通过访问控制符实现
  • 多态:???

多态这个概念比较神秘。以前看到一个解释是这么说的:

继承是子类引用父类方法;多态是父类引用子类方法。

当时看了觉得有点奇怪,父类怎么能引用子类方法呢?现在我的理解是如下:

子类对象继承了父类方法,但是可能又对继承过来的父类方法进行了重写覆盖。而一切出现父类的地方都可以用其子类向上转型进行替换(里氏替换原则),调用方法时调用的会是在子类里被重写的子类方法。继承、重写和向上转型,这是实现多态的三要素。

这样,如果一个父类有多个子类,它们又分别用不同方式重写了继承下来的同一个方法,那么在把这些子类都当父类使用时,就会展现出多态性:明明是对同一个类的对象调用了同一个方法,但行为却完全不同。

举个例子,假如有个扬声器类,包含一个大叫()方法,调用时会“嘤嘤嘤”;而继承自它的有大叫“洒↓比↑”的喇叭类和大叫“哼,哼,啊啊啊啊啊啊啊”的野兽先辈类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//这段代码甚至真的可以跑,C#支持用汉字做标识符,大概是因为用了Unicode
using System;
using System.Collections.Generic;

namespace PlayGround
{
class 扬声器
{
public virtual void 大叫() => Console.WriteLine("嘤嘤嘤");
}

class 喇叭 : 扬声器
{
public override void 大叫() => Console.WriteLine("洒↓比↑");
}

class 野兽先辈 : 扬声器
{
public override void 大叫() => Console.WriteLine("哼,哼,啊啊啊啊啊啊啊");
}

class 屑狐狸 : 扬声器
{
public override void 大叫() => Console.WriteLine("呜呜呜呜,好可怜啊");
}

class Program
{
static void Main(string[] args)
{
var 一堆扬声器 = new List<扬声器>() { new 扬声器(), new 喇叭(), new 野兽先辈(), new 屑狐狸() };

foreach (var 某个扬声器 in 一堆扬声器)
{
某个扬声器.大叫();
// 输出:
// 嘤嘤嘤
// 洒↓比↑
// 哼,哼,啊啊啊啊啊啊啊
// 呜呜呜呜,好可怜啊
}
}
}
}
阅读全文 »

以下所介绍的部分命令,在设备上是由适合嵌入式 Linux 的工具集 BusyBox 提供,行为和 Linux 桌面发行版上不完全一致,这里均采用 BusyBox 所提供的指令用法。

ifconfig:查看接口状态

ifconfig 可设置网卡接口的状态,或是显示目前的设置。以下是一些主要的设置参数。

语法格式:ifconfig < -a > [网卡名称] <参数>

参数 作用
add <地址> 设置网卡的IPv6地址。
del <地址> 删除网卡的IPv6地址。
up 启动指定的网卡。
down 关闭指定的网卡。
metric <数目> 指定在计算数据包的转送次数时,所要加上的数目。
mtu <字节> 设置网卡的MTU(最大数据包大小)。
netmask <子网掩码> 设置网卡的子网掩码。
-broadcast <地址> 将要送往指定地址的数据包当成广播数据包来处理。
[IP地址] 指定网卡的IP地址。

直接输入 ifconfig 就会列出目前已激活的网卡,不论这个卡是否有给予 IP ,都会被显示出来。带接口的 ifconfig eth0 则仅会表示接口 eth0 的相关数据,无论其是否启动。使用 -a 选项,则会展示所有网卡的属性。

示例

1
2
3
4
5
6
7
8
$ ifconfig ra8
ra8 Link encap:Ethernet HWaddr 02:A1:BB:01:23:48
inet6 addr: fe80::a1:bbff:fe01:2348/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:130 errors:0 dropped:0 overruns:0 frame:0
TX packets:61 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:13971 (13.6 KiB) TX bytes:7767 (7.5 KiB)
阅读全文 »