前言
UniRx中由很多操作符,注意要分为三类
- Linq操作符,和Linq语法风格一致
- Rx操作符,从Rx.Net库继承下来的操作符。
- UniRx操作符,UniRx针对Unity的独有操作符。
Rx和Linq
Linq是微软的一项技术,新增一种自然查询的SQL语法到.NetFramework的编程语言中。
Rx最初是微软Linq的一个扩展库,让开发者可以利用可观察序列和Linq风格查询操作符编写异步和基于事件的程序。2012年被开源了。目标是提供一致性的编程接口,帮助开发者方便的处理异步数据流。Rx支持.Net,JS,Java,C++等,近几年越来越流行,已经支持几乎全部的流行编程语言。Rx目前大部分语言库是由Rx组织负责维护。Rx的Unity版本就是UniRx。
Rx.io 官方给的定义是:Rx 是一个使⽤可观察数据流进行异步编程的编程接⼝。
我们可以这么来理解:Rx=Observables+Linq+Schedulers,Rx结合了观察者模式,迭代器模式和函数式编程的精华。
Rx的编程风格几乎是和Linq一致的,所以我们从Linq操作符来学习Rx是最合适不过的了。
操作符
where
LINQ 中的 Where 操作符与 SQL 命令中的 Where 作用相似,都是起到范围限定也就是过滤作用的,而
条判断条件就是它后面所接的子句
- LINQ Where
// 查询⼤大于 45 岁的学⽣生:
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 50},
new Student() {Name = "李李四", Age = 40}
};
var oldStudents = students.Where(student => student.Age > 45);
foreach (var student in oldStudents)
{
Debug.Log(student.Name);
}
输出结果
// 结果
// 张三
// 张三
- UniRx Where
Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonDown(0))
.Subscribe(_ => { Debug.Log("mouse down"); })
.AddTo(this);
鼠标左键点击打印mouse down
Select
LINQ 中的 Select 操作符 与 SQL 命令中的 Select 作用相似,但是位置不同,查询表达式中的 select 及
所接子句是放在最后并把子句中的变量也就是结果返回来
- LINQ Select
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 50},
new Student() {Name = "李李四", Age = 40}
};
var oldStudentNames = students.Where(student => student.Age > 45)
.Select(student => student.Name);
foreach (var studentName in oldStudentNames)
{
Debug.Log(studentName);
}
输出结果为:
张三
张三
- UniRx Select
Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonUp(0))
.Select(_ => "mouse up")
.Subscribe(Debug.Log)
.AddTo(this);
当每次抬起⿏鼠标左键,则输出 mouse up。
First
取序列中的第一个元素。First 有两种形式,一种是直接获取第一个元素,第二种则是取序列中满足条件
的第一个元素
- LINQ First
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 50},
new Student() {Name = "李李四", Age = 40}
};
var oldStudent = students
.Where(student => student.Age > 45)
.First();
Debug.Log(oldStudent.Name);
输出结果为: 张三
- UniRx First
Observable.EveryUpdate()
.First(_ => Input.GetMouseButtonDown(0))
.Subscribe(_ => { Debug.Log("mouse down"); })
.AddTo(this);
当⿏标点击按下的时候,输出 mouse down。
Distinct
筛选序列中不相同的值。⽤用于查询不重复的结果集。生成 SQL 语句为: SELECT DISTINCT [City] FROM
[Customers]
- LINQ Distinct
var names = new List<string>()
{
"张三",
"张三",
"李李四",
};
var distinctNames = names.Distinct();
foreach (var distinctName in distinctNames)
{
Debug.Log(distinctName);
}
输出结果为:
张三
李李四
- UniRx Distinct
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 50},
new Student() {Name = "李李四", Age = 40}
};
students.ToObservable()
.Distinct(student=>student.Name)
.Subscribe(student =>
{
Debug.Log(student.Name);
});
var leftClickStream = Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonDown(0))
.Select(_ => "left clicked");
var rightClickStream = Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonDown(1))
.Select(_ => "right clicked");
Observable.Merge(leftClickStream,rightClickStream)
.Distinct()
.Subscribe(Debug.Log)
.AddTo(this);
不管点击多少次鼠标左键,还是鼠标右键,输出只输出一次 “left clicked” 和 “right
clicked”
Last
取序列列中的最后⼀一个元素
- LINQ Last
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 50},
new Student() {Name = "李四", Age = 40}
};
var lastStudent = students.Last();
Debug.Log(lastStudent.Name);
输出结果为
李四
- UniRx Last
new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 45},
new Student() {Name = "李四", Age = 50}
}
.ToObservable()
.Last(student => student.Age == 50)
.Subscribe(student => { Debug.Log(student.Name); });
输出结果为:
李四
SelectMany
将序列的每个元素投影到 IEnumerable 并将结果序列合并为一个序列。对每项再进行行遍历处理再进行行合成序列
- LINQ SelectMany
var students = new List<Student>()
{
new Student() {Name = "张三", Age = 50},
new Student() {Name = "张三", Age = 45},
new Student() {Name = "李四", Age = 50}
}
var singleChars = students.SelectMany(student => student.Name + ":" +
student.Age);
foreach (var singleChar in singleChars)
{
Debug.Log(singleChar);
}
输出结果为: 张三 : 50 张三 : 45 李四 : 50
- UniRx SelectMany
IEnumerator A()
{
yield return new WaitForSeconds(1.0f);
Debug.Log("A");
}
IEnumerator B()
{
yield return new WaitForSeconds(2.0f);
Debug.Log("B");
}
IEnumerator C()
{
yield return new WaitForSeconds(3.0f);
Debug.Log("C");
}
var streamA = Observable.FromCoroutine(A);
var streamB = Observable.FromCoroutine(B);
var streamC = Observable.FromCoroutine(C);
streamA.SelectMany(streamB.SelectMany(streamC))
.Subscribe(_ => Debug.Log("Hello"));
输出结果为:
A
B
C
Hello
Take
从序列列的开头返回指定数量量的相邻元素。
- LINQ Take
int[] grades = { 59, 82, 70, 56, 92, 98, 85 };
var topThreeGrades = grades.OrderByDescending(grade => grade) // 根据分数降序排序
.Take(3);
Debug.Log("分数前三名的是:");
foreach (var topThreeGrade in topThreeGrades)
{
Debug.Log(topThreeGrade);
}
输出结果为:
分数前三名的是:
98
92
85
- UniRx Take
this.UpdateAsObservable()
.Where(_=>Input.GetMouseButtonDown(0))
.Take(5)
.Subscribe(_ => Debug.Log(1));
输出结果为,只有前 5 次⿏鼠标点击才会输出 1。
Concat
连接两个序列列。
- LINQ Concat
Pet[] cats =
{
new Pet {Name = "Barley", Age = 8},
new Pet {Name = "Boots", Age = 4},
new Pet {Name = "Whiskers", Age = 1}
};
Pet[] dogs =
{
new Pet {Name = "Bounder", Age = 3},
new Pet {Name = "Snoopy", Age = 14},
new Pet {Name = "Fido", Age = 9}
};
var petNames = cats.Select(cat => cat.Name).Concat(dogs.Select(dog => dog.Name));
foreach (var petName in petNames)
{
Debug.Log(petName);
}
输出结果:
Barley
Boots
Whiskers
Bounder
Snoopy
Fido
- UniRx Concat
var a = this.UpdateAsObservable().Take(3).Select(_ => "A");
var b = this.UpdateAsObservable().Take(2).Select(_ => "B");
var c = a.Concat(b);
c.Subscribe(Debug.Log);
输出结果:
A
A
A
B
B
WhenAll
确定序列列中的所有元素是否都满⾜足条件。
- LINQ All
Pet[] pets =
{
new Pet {Name = "Barley", Age = 10},
new Pet {Name = "Boots", Age = 4},
new Pet {Name = "Whiskers", Age = 6}
};
var allStartWithB = pets.All(pet =>
pet.Name.StartsWith("B"));
Debug.LogFormat("{0} pet names start with 'B'.",allStartWithB ? "All" : "Not all");
输出结果为:
Not all pet names start with 'B'.
- UniRx WhenAll
var streamA = Observable.FromCoroutine(A);
var streamB = Observable.FromCoroutine(B);
var streamC = Observable.FromCoroutine(C);
Observable.WhenAll(streamA, streamB, streamC)
.Subscribe(_ => { Debug.Log("Completed"); });
输出结果为(A、B、C 输出顺序不不⼀一定):
B
C
A
Completed
OfType
OfType根据指定类型筛选 IEnumerable 的元素。
- LINQ OfType
var list = new ArrayList {30, 30.0f, "test"};
var filterList = list.OfType<float>();
foreach (var obj in filterList)
{
Debug.Log(obj);
}
输出结果为:
30
- UniRx OfType
// 创建一个 Subject(Observable)
var objects = new Subject<object>();
// 订阅该 Observable,进行行类型过滤
objects.OfType<object, string>()
.Subscribe(Debug.Log);
// 手动发送数据
objects.OnNext(1);
objects.OnNext(2);
objects.OnNext("3");
objects.OnNext(4);
// 手动结束
objects.OnCompleted();
输出结果为:
3
Cast
将 IEnumerable 的元素强制转换为指定的类型
- LINQ Cast
var fruits = new ArrayList {"mango", "apple", "lemon"};
var fruitNames = fruits.Cast<string>();
// 等同于
// var fruitNames = fruits.Select(fruit => fruit.ToString);
foreach (var fruit in fruitNames)
{
Debug.Log(fruit);
}
输出代码为:
mango
apple
lemon
- UniRx Cast
// 创建⼀一个 Subject(Observable)
var objects = new Subject<object>();
// 订阅该 Observable,进⾏行行类型转换
objects.Cast<object,int>().Subscribe(i => Debug.Log(i));
// ⼿手动发送数据
objects.OnNext("1");
objects.OnNext("2");
objects.OnNext(3);
// ⼿手动结束
objects.OnCompleted();
输出结果为:
1
2
3
GroupBy
对序列列中的元素进⾏行行分组
- LINQ GroupBy
var students = new List<Student>()
{
new Student {Name = "张三", Age = 50},
new Student {Name = "张三", Age = 50},
new Student {Name = "李四", Age = 40}
};
var studentGroup4Names = students.GroupBy(student => student.Name);
foreach (var studentGroup in studentGroup4Names)
{
Debug.LogFormat("Group Key:{0}", studentGroup.Key);
foreach (var student in studentGroup)
{
Debug.LogFormat("Name:{0} Age:{1}", student.Name, student.Age);
}
}
输出结果为:
Group Key:张三
Name:张三 Age:50
Name:张三 Age:50
Group Key:李四
Name:李四 Age:40
- UniRx GroupBy
Observable.Interval(TimeSpan.FromSeconds(0.1))
.Take(10)
.GroupBy(i => i % 3)
.Subscribe(group =>
{
group.Subscribe(number => {
Debug.LogFormat("Key:{0},Number:{1}",group.Key, number);
});
});
输出结果为:
Key:0,Number:0
Key:1,Number:1
Key:2,Number:2
Key:0,Number:3
Key:1,Number:4
Key:2,Number:5
Key:0,Number:6
Key:1,Number:7
Key:2,Number:8
Key:0,Number:9
Range
生成指定范围内的整数的序列
- LINQ Range
var squares = Enumerable.Range(5, 3).Select(x => x * x);
foreach (var square in squares)
{
Debug.Log(square);
}
输出结果:
25
36
49
- UnIRx Range
var squares = Observable.Range(5, 3).Select(x => x * x);
squares.Subscribe(square => { Debug.Log(square); });
输出结果为
25
36
48
Skip
跳过序列中指定数量的元素,然后返回剩余的元素。
- LINQ Skip
int[] grades = {59, 82, 70, 56, 92, 98, 85};
var lowerGrades =
grades.OrderByDescending(g => g).Skip(3);
foreach (var grade in lowerGrades)
{
Debug.Log(grade);
}
输出结果为
82
70
59
56
- UniRx Skip
this.UpdateAsObservable()
.Where(_ => Input.GetMouseButtonDown(0))
.Skip(5)
.Subscribe(_ => { Debug.Log("mouse clicked"); });
输出结果为,鼠标点击第六次时,才开始输出
”mouse clicked”
TakeWhile
如果指定的条件为 true,则返回序列列中的元素,然后跳过剩余的元素。
- LINQ TakeWhile
var fruits = new[]
{
"apple", "banana", "mango", "orange","passionfruit", "grape"
};
var fruitsAfterOrange = fruits.TakeWhile(fruit => fruit != "orange");
foreach (var fruit in fruitsAfterOrange)
{
Debug.Log(fruit);
}
输出结果为
apple
banana
mango
- UniRx TakeWhile
this.UpdateAsObservable()
.TakeWhile(l => !Input.GetMouseButton(0))
.Subscribe(_ => Debug.Log("before mouse clicked"));
运行结果为,持续输出”before mouse clicked”,当鼠标点击之后不再输出 “before mouse clicked”
SkipWhile
如果指定的条件为 true,则跳过序列列中的元素,然后返回剩余的元素。
- LINQ SkipWhile
int[] grades = {59, 82, 70, 56, 92, 98, 85};
var lowerGrades = grades.OrderByDescending(grade => grade)
.SkipWhile(grade => grade >= 80);
foreach (var grade in lowerGrades)
{
Debug.Log(grade);
}
输出结果为:
70
59
56
- UniRx SkipWhile
this.UpdateAsObservable()
.SkipWhile(_ => !Input.GetMouseButton(0))
.Subscribe(_ => { Debug.Log("mouse button down"); });
当点击鼠标后,持续输出
mouse button down
Zip (.Net 4)
将指定函数应用于两个序列的对应元素,以生成结果序列。
- LINQ Zip
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " +
second);
foreach (var item in numbersAndWords)
Debug.Log(item);
输出结果为:
1 one
2 two
3 three
- UniRx Zip
var rightStream = this.UpdateAsObservable().Where(_ =>
Input.GetMouseButtonDown(0));
var leftStream = this.UpdateAsObservable().Where(_ =>
Input.GetMouseButtonDown(1));
leftStream
.Zip(rightStream, (l, r) => Unit.Default)
.Subscribe(_ => { Debug.Log("ok"); });
运行之后,点击鼠标的顺序为, 左->右->左->右->左->左->左->右->右->右
输出的结果为:
// 左
ok // 右
// 左
ok // 右
// 左
// 左
// 左
ok // 右
ok // 右
ok // 右
Repeat
在生成序列中重复该值的次数
- LINQ Repeat
var strings = Enumerable.Repeat("I like programming.", 5);
foreach (var str in strings)
{
Debug.Log(str);
}
输出结果为:
I Like programming.
I Like programming.
I Like programming.
I Like programming.
I Like programming.
- UniRx Repeat
var source = Observable.Range(0, 3);
var result = source.Repeat(3);
result.Subscribe(Debug.Log,() => Debug.Log("Completed"));
运⾏之后,输出的结果为:
0
1
2
0
1
2
0
1
2
Completed
TakeLast
获取序列的最后几项
- LINQ TakeLast
- UniRx TakeLast
int[] grades = {59, 82, 70, 56, 92, 98, 85};
var bottomThreeGrades = grades.OrderByDescending(grade=>grade)
.ToObservable()
.TakeLast(3);
bottomThreeGrades.Subscribe(buttomThreeGrade => Debug.Log(buttomThreeGrade));
输出结果为
70
59
56
Single /Default
返回序列中的单个特定元素,与 First 非常类似,但是 Single 要确保其满足条件的元素在序列中只有一
个。
- LINQ Single
string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" };
var fruit1 = fruits.Single(fruit => fruit.Length > 10);
Debug.Log(fruit1);
输出结果为
passionfruit
- UniRx Single
string[] fruits = { "apple", "banana", "mango","orange", "passionfruit", "grape" };
fruits.ToObservable().Single(fruit => fruit.Length > 10)
.Subscribe(Debug.Log);
输出结果为
passionfruit
ToArray
从 IEnumerable 中创建数组。
- LINQ ToArray
var packages =
new List<Package>
{
new Package {Company = "Coho Vineyard", Weight = 25.2},
new Package {Company = "Lucerne Publishing", Weight = 18.7},
new Package {Company = "Wingtip Toys", Weight = 6.0},
new Package {Company = "Adventure Works", Weight = 33.8}
};
var companies = packages.Select(pkg => pkg.Company).ToArray();
foreach (var company in companies)
{
Debug.Log(company);
}
输出结果:
Coho Vineyard
Lucerne Publishing
Wingtip Toys
Adventure Works
- UniRx ToArray
var period = TimeSpan.FromMilliseconds(200);
var source = Observable.Timer(TimeSpan.Zero, period).Take(5);
var result = source.ToArray();
result.Subscribe(arr => {
Debug.Log("Received array");
foreach (var value in arr)
{
Debug.Log(value);
}
}, () => Debug.Log("Completed"));
Debug.Log("Subscribed");
输出结果为:
Subscribed
Received array
0
1
2
3
4
Completed
ToList
从 IEnumerable 创建⼀一个 List。
- LINQ ToList
string[] fruits = { "apple", "passionfruit", "banana", "mango","orange", "blueberry", "grape", "strawberry" };
var lengths = fruits.Select(fruit => fruit.Length).ToList();
Debug.Log(lengths.GetType());
foreach (var length in lengths)
{
Debug.Log(length);
}
输出结果:
System.Collections.Generic.List`1[System.Int32]
5
12
6
5
6
9
5
10
- UniRx ToList
var subject = new Subject<int>();
subject.ToList().Subscribe(intList =>
{
Debug.Log(intList.GetType().ToString());
foreach (var i in intList)
{
Debug.Log(i);
}
});
subject.OnNext(1);
subject.OnNext(2);
subject.OnNext(3);
subject.OnCompleted();
输出结果为
System.Collections.Generic.List`1[System.Int32]
1
2
3
Aggregate
对序列应用累加器函数。 将指定的种子值用作累加器的初始值,并使用指定的函数选择结果值。
- LINQ Aggregate
var numbers = new[] {1, 2, 3, 4, 5};
var result = numbers.Aggregate(
(total, next) => total * next);
Debug.LogFormat("5的阶乘为:{0}", result); //返回120,也就是1*2*3*4*5
输出结果为:
5的阶乘为:120
- UniRx Aggregate
Observable.Range(0, 8)
.Aggregate(0,(acc,currentValue)=>acc+5)
.Subscribe(xx =>
{
Debug.Log(xx);
});
输出结果为:
40
Empty
返回具有指定类型参数的空 IEnumerable
- LINQ Empty
string[] names1 = { "Hartono, Tommy" };
string[] names2 = { "Adams, Terry", "Andersen, Henriette Thaulow",
"Hedlund, Magnus", "Ito, Shu" };
string[] names3 = { "Solanki, Ajay", "Hoeing, Helge",
"Andersen, Henriette Thaulow",
"Potra, Cristina", "Iallo, Lucio" };
var namesList = new List<string[]> { names1, names2, names3 };
var allNames = namesList.Aggregate(Enumerable.Empty<string>(),
(current, next) => next.Length > 3 ? current.Union(next) :
current);
foreach (var name in allNames)
{
Debug.Log(name);
}
输出结果为:
Adams, Terry
Andersen, Henriette Thaulow
Hedlund, Magnus
Ito, Shu
Solanki, Ajay
Hoeing, Helge
Potra, Cristina
Iallo, Lucio
- UniRx Empty
创建一个不发出任何数据,但正常终止的Observable
var s = Observable.Empty<Unit>();
s.Subscribe(e => Debug.Log("e: " + e), () => Debug.Log("OnCompleted"));
输出结果为
OnCompleted