0%

豆瓣评分8.6

前段时间华为推出了10.3寸的墨水屏平板Matepad Paper,似乎非常贴合我对“平板”这一形态的需求。毕竟我手上的Matepad 11原本几乎唯一的作用就是阅读,而基本告别娱乐属性的墨水屏是强化这一属性最好不过的配置了。不过由于了解到华为对PDF的优化并不到位,阅读灯也没有色温调节,我还是把Matepad换成了文石的BOOX NoteX。

BOOX NoteX

在把那些买来就基本没读过的书来来去去搬过几回之后,我的纸质书“情怀”就转化为了对科技的崇尚。我决定不再购买纸质书,而是尽可能获取epub/pdf的版本,Z-Library全国图书馆参考咨询联盟也给了我这么做的充足底气,现在也有了这台不错的设备,没有理由不把阅读这件事捡起来了。

有多久没有从头到尾读完一整本书了呢?似乎大学以后,阅读就是为了查阅资料,或者在碎片时间里读些短篇故事,这也许也是数年间我的认知水平与思考深度未曾有过提升的原因。之后也许会试着每两周左右读一本书吧。

那么第一本书就是Z-Library推荐的《被讨厌的勇气》。它初看像是又一卷成功学厕纸,但读过之后却发现意外地有道理也有趣味。

这本书仿照类似柏拉图《理想国》的对话形式,以“青年”与“哲人”的辩论引出心理学家阿尔弗雷德·阿德勒(Alfred Adler)的理论,以图让读者找到“幸福”的真谛。

阅读全文 »

该文章译自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 一堆扬声器)
{
某个扬声器.大叫();
// 输出:
// 嘤嘤嘤
// 洒↓比↑
// 哼,哼,啊啊啊啊啊啊啊
// 呜呜呜呜,好可怜啊
}
}
}
}
阅读全文 »