ABP框架学习记录(16)- Validation的实现

Validation 在ABP项目中的位置:

QQ截图20190812145325.png

定义

ValidatorAttribute:自定义属性,继承 Attribute

IValueValidator:定义值验证接口;

ValueValidatorBase:默认实现 IValueValidator 接口,提供抽象基类;

QQ截图20190820165721.png

StringValueValidator:验证字符串类型;

BooleanValueValidator:验证布尔类型;

NumericValueValidator:验证数字类型;

DisableValidationAttribute:禁止自动验证属性,适用于方法,类,属性;

EnableValidationAttribute:启用验证,可以在禁止验证的类中的方法添加,以启动自动验证;

ICustomValidate:提供自定义验证接口,自定义验证类必须实现此接口;

QQ截图20190820172950.png

CustomValidationContext:自定义验证上下文;

QQ截图20190820181037.png

IMethodParameterValidator:定义验证方法参数的接口;

QQ截图20190821135444.png

CustomValidator:实现 IMethodParameterValidator 接口;自定义验证

QQ截图20190821140040.png

ValidatableObjectValidator:对象验证器。确定指定的对象是否有效。

QQ截图20190821141036.png

DataAnnotationsValidator:数据注释验证器

QQ截图20190821142048.png

IShouldNormalize:此接口用于在方法执行之前规范化输入。

QQ截图20190821144206.png

QQ截图20190821144655.png

初始化

AbpBootstrapperAddInterceptorRegistrars 方法初始化验证拦截器:

QQ截图20190821142450.png

ValidationInterceptorRegistrar:验证拦截器注册。

QQ截图20190821142924.png

ValidationInterceptor:验证拦截器。

QQ截图20190821143004.png

MethodInvocationValidator:验证方法参数。

QQ截图20190821143536.png

AbpKernelModule 的预初始化方法 PreInitialize,调用 AddMethodParameterValidators

QQ截图20190821142706.png

ABP框架学习记录(15)- DTO的设计

DTO在项目中的目录位置:

QQ截图20190812131158.png

DTO 基础接口,类:

IEntityDto<TPrimaryKey>:定义公共的属性接口,此接口为泛型接口;

IEntityDto:以Int类型的主键类型接口;

EntityDto<TPrimaryKey>:实现 IEntityDto<TPrimaryKey>泛型接口;

EntityDto:实现 EntityDto<int>, IEntityDto 接口;

ComboboxItemDto:复选框DTO;

此外,还有审计相关DTO:

CreationAuditedEntityDto<TPrimaryKey>CreationAuditedEntityDto,

FullAuditedEntityDto<TPrimaryKey>FullAuditedEntityDto;

IPagedResult:实现 IListResult<T>IHasTotalCount,此接口定义输出到客户端的分页标准;

PagedResultDto<T>:实现 ListResultDto<T>, IPagedResult<T>接口;

IListResult<T>:定义输出到客户端集合的接口;

ListResultDto<T>:实现 IListResult<T> 接口;

线性表之-单链表

结构描述:

1,Data 数据 + Next 指针,组成一个单链表的内存结构 ;
2,第一个内存结构称为 链头,最后一个内存结构称为 链尾;
3,链尾的 Next 指针设置为 NULL [指向空];
4,单链表的遍历方向单一【只能从链头一直遍历到链尾】

IListDataStructure<T>泛型接口

首先定义 IListDataStructure<T>泛型接口:

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
public interface IListDataStructure<T>
{
//取得线性表的实际元素个数
int Count();

//清空线性表
void Clear();

//判断线性表是否为空
bool IsEmpty();

//(在末端)追加元素
void Append(T item);

//在位置i“前面”插入元素item
void InsertBefore(T item, int i);

//在位置i“后面”插入元素item
void InsertAfter(T item, int i);

//删除索引i处的元素
T RemoveAt(int i);

//获得索引位置i处的元素
T GetItemAt(int i);

//返回元素value的索引
int IndexOf(T value);

//反转线性表的所有元素
void Reverse();
}

Node<T>

定义 Node<T> 泛型类

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
public class Node<T>
{
private T data;
private Node<T> next;

public Node(T val, Node<T> p)
{
data = val;
next = p;
}

public Node(Node<T> p)
{
next = p;
}

public Node(T val)
{
data = val;
next = null;
}

public Node()
{
data = default(T);
next = null;
}

public T Data
{
get { return data; }
set { data = value; }
}

public Node<T> Next
{
get { return next; }
set { next = value; }
}
}

定义 LinkList<T> 单链表:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
public class LinkList<T> : IListDataStructure<T>
{
private Node<T> head;

public Node<T> Head
{
get { return head; }
set { head = value; }
}

public LinkList()
{
head = null;
}

/// <summary>
/// 类索引器
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get
{
return this.GetItemAt(index);
}
}

/// <summary>
/// 返回单链表的长度
/// </summary>
/// <returns></returns>
public int Count()
{
Node<T> p = head;
int len = 0;
while (p != null)
{
len++;
p = p.Next;
}
return len;
}

/// <summary>
/// 清空
/// </summary>
public void Clear()
{
head = null;
}

/// <summary>
/// 是否为空
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
return head == null;
}

/// <summary>
/// 在最后附加元素
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
Node<T> d = new Node<T>(item);
Node<T> n = new Node<T>();

if (head == null)
{
head = d;
return;
}

n = head;
while (n.Next != null)
{
n = n.Next;
}
n.Next = d;
}

//前插
public void InsertBefore(T item, int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("List is empty or Position is error!");
return;
}

//在最开头插入
if (i == 0)
{
Node<T> q = new Node<T>(item);
q.Next = Head;//把"头"改成第二个元素
head = q;//把自己设置为"头"
return;
}

Node<T> n = head;
Node<T> d = new Node<T>();
int j = 0;

//找到位置i的前一个元素d
while (n.Next != null && j < i)
{
d = n;
n = n.Next;
j++;
}

if (n.Next == null) //说明是在最后节点插入(即追加)
{
Node<T> q = new Node<T>(item);
n.Next = q;
q.Next = null;
}
else
{
if (j == i)
{
Node<T> q = new Node<T>(item);
d.Next = q;
q.Next = n;
}
}
}

/// <summary>
/// 在位置i后插入元素item
/// </summary>
/// <param name="item"></param>
/// <param name="i"></param>
public void InsertAfter(T item, int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("List is empty or Position is error!");
return;
}

if (i == 0)
{
Node<T> q = new Node<T>(item);
q.Next = head.Next;
head.Next = q;
return;
}

Node<T> p = head;
int j = 0;

while (p != null && j < i)
{
p = p.Next;
j++;
}
if (j == i)
{
Node<T> q = new Node<T>(item);
q.Next = p.Next;
p.Next = q;
}
else
{
Console.WriteLine("Position is error!");
}
}

/// <summary>
/// 删除位置i的元素
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public T RemoveAt(int i)
{
if (IsEmpty() || i < 0)
{
Console.WriteLine("Link is empty or Position is error!");
return default(T);
}

Node<T> q = new Node<T>();
if (i == 0)
{
q = head;
head = head.Next;
return q.Data;
}

Node<T> p = head;
int j = 0;

while (p.Next != null && j < i)
{
j++;
q = p;
p = p.Next;
}

if (j == i)
{
q.Next = p.Next;
return p.Data;
}
else
{
Console.WriteLine("The node is not exist!");
return default(T);
}
}

/// <summary>
/// 获取指定位置的元素
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public T GetItemAt(int i)
{
if (IsEmpty())
{
Console.WriteLine("List is empty!");
return default(T);
}

Node<T> p = new Node<T>();
p = head;

if (i == 0)
{
return p.Data;
}

int j = 0;

while (p.Next != null && j < i)
{
j++;
p = p.Next;
}

if (j == i)
{
return p.Data;
}
else
{
Console.WriteLine("The node is not exist!");
return default(T);
}
}

//按元素值查找索引
public int IndexOf(T value)
{
if (IsEmpty())
{
Console.WriteLine("List is Empty!");
return -1;
}
Node<T> p = new Node<T>();
p = head;
int i = 0;
while (!p.Data.Equals(value) && p.Next != null)
{
p = p.Next;
i++;
}
return i;
}

/// <summary>
/// 元素反转
/// </summary>
public void Reverse()
{
LinkList<T> result = new LinkList<T>();
Node<T> t = this.head;
result.Head = new Node<T>(t.Data);
t = t.Next;

//(把当前链接的元素从head开始遍历,逐个插入到另一个空链表中,这样得到的新链表正好元素顺序跟原链表是相反的)
while (t != null)
{
result.InsertBefore(t.Data, 0);
t = t.Next;
}
this.head = result.head;//将原链表直接挂到"反转后的链表"上
result = null;//显式清空原链表的引用,以便让GC能直接回收
}

public override string ToString()
{
StringBuilder sb = new StringBuilder();
Node<T> n = this.head;
sb.Append(n.Data + ",");
while (n.Next != null)
{
sb.Append(n.Next.Data + ",");
n = n.Next;
}
return sb.ToString().TrimEnd(',');
}
}

使用:在Main() 方法调用 TestLinkList() 方法:

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
public static void TestLinkList()
{
Console.WriteLine("-------------------------------------");
Console.WriteLine("单链表测试开始...");
LinkList<string> link = new LinkList<string>();
link.Head = new Node<string>("x");
link.InsertBefore("w", 0);
link.InsertBefore("v", 0);
link.Append("y");
link.InsertBefore("z", link.Count());
Console.WriteLine(link.Count());//5
Console.WriteLine(link.ToString());//v,w,x,y,z
Console.WriteLine(link[1]);//w
Console.WriteLine(link[0]);//v
Console.WriteLine(link[4]);//z
Console.WriteLine(link.IndexOf("z"));//4
Console.WriteLine(link.RemoveAt(2));//x
Console.WriteLine(link.ToString());//v,w,y,z
link.InsertBefore("x", 2);
Console.WriteLine(link.ToString());//v,w,x,y,z
Console.WriteLine(link.GetItemAt(2));//x
link.Reverse();
Console.WriteLine(link.ToString());//z,y,x,w,v
link.InsertAfter("1", 0);
link.InsertAfter("2", 1);
link.InsertAfter("6", 5);
link.InsertAfter("8", 7);
link.InsertAfter("A", 10);//Position is error!

Console.WriteLine(link.ToString()); //z,1,2,y,x,w,6,v,8
}

参考:

数据结构C#版笔记–单链表(LinkList)

线性表

  线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。

  线性表(List)是线性结构的一种典型实现,它又可以分为:顺序表(SeqList)和链表(LinkList)二大类。

命名空间 System.Collections.Generic 中的 List<T> 就是一个内置的顺序表。

  线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储),但是把最后一个数据元素的尾指针指向了首位结点)。

  我们说“线性”和“非线性”,只在逻辑层次上讨论,而不考虑存储层次,所以双向链表和循环链表依旧是线性表。

  在数据结构逻辑层次上细分,线性表可分为一般线性表和受限线性表。一般线性表也就是我们通常所说的“线性表”,可以自由的删除或添加结点。受限线性表主要包括栈和队列,受限表示对结点的操作受限制。

  线性表主要由顺序表示或链式表示。在实际应用中,常以栈、队列、字符串等特殊形式使用。

  顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,称为线性表的顺序存储结构或顺序映像(sequential mapping)。它以“物理位置相邻”来表示线性表中数据元素间的逻辑关系,可随机存取表中任一元素。

  链式表示指的是用一组任意的存储单元存储线性表中的数据元素,称为线性表的链式存储结构。它的存储单元可以是连续的,也可以是不连续的。在表示数据元素之间的逻辑关系时,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置),这两部分信息组成数据元素的存储映像,称为结点(node)。它包括两个域;存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称为指针或链。

参考:

线性表

链表

链表是线性表的一种。

  链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

  由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表有很多种不同的类型:单向链表,双向链表以及循环链表。如C# 默认提供 LinkedList<T> 就是一个双向链表。

QQ截图20190729145028.png

特点

  线性表的链式存储表示的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素 与其直接后继数据元素之间的逻辑关系,对数据元素来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。由这两部分信息组成一个”结点”,表示线性表中一个数据元素。线性表的链式存储表示,有一个缺点就是要找一个数,必须要从头开始找起,十分麻烦。

  存储数据元素信息的域称作数据域(设域名为data),存储直接后继存储位置的域称为指针域(设域名为next)。指针域中存储的信息又称做指针或链。

单链表

结构描述:

1,Data 数据 + Next 指针,组成一个单链表的内存结构 ;
2,第一个内存结构称为 链头,最后一个内存结构称为 链尾;
3,链尾的 Next 指针设置为 NULL [指向空];
4,单链表的遍历方向单一【只能从链头一直遍历到链尾】

插入/删除操作:

1411747-85d91bb20b2dede7.png

双向链表

结构描述:
1,Data 数据 + Next 指针 + Prev 指针,组成一个双向链表的内存结构;
2,第一个内存结构称为 链头,最后一个内存结构称为 链尾;
3,链头的 Prev 指针设置为 NULL, 链尾的 Next 指针设置为 NULL;
4,Prev 指向的内存结构称为 前驱, Next 指向的内存结构称为 后继;
5,双向链表的遍历是双向的,即如果把从链头的 Next 一直到链尾的[NULL] 遍历方向定义为正向,那么从链尾的 Prev 一直到链头 [NULL ]遍历方向就是反向;

1411747-f126e2b10d24395f.png

循环链表

1,循环链表分为单向、双向两种;
2,单向的实现就是在单链表的基础上,把链尾的 Next 指针直接指向链头,形成一个闭环;
3,双向的实现就是在双向链表的基础上,把链尾的 Next 指针指向链头,再把链头的 Prev 指针指向链尾,形成一个闭环;
4,循环链表没有链头和链尾的说法,因为是闭环的,所以每一个内存结构都可以充当链头和链尾;

1411747-6d7e9c9f5882ac81.png

参考:

百度百科-链表

数据结构:链表

时间复杂度 O(log n) 意味着什么?

算法复杂度中的O(logN)底数是多少

ABP框架学习记录(14)- Auditing的实现

Auditing审计跟踪(也叫审计日志)是与安全相关的信息按照时间顺序的记录,它们提供了活动序列的文档证据,这些活动序列可以在任何时间影响一个特定的操作。

Auditing 在 ABP 项目中的位置:

QQ截图20190727141502.png

基础定义

AuditInfo:定义需要被审计信息实体类;

AuditedAttribute:用于标识一个方法或一个类的所有方法都需要启用Auditing功能。

DisableAuditingAttribute:用于标识一个方法或一个类的所有方法都需要关闭Auditing功能。

IAuditSerializer:提供审计序列化接口;

JsonNetAuditSerializerIAuditSerializer 接口的默认实现,提供序列化方法;

IAuditInfoProvider:提供 Fill 方法,完善 AuditInfo 信息。

DefaultAuditInfoProviderIAuditInfoProvider 的默认实现;

IClientInfoProvider:提供客户端信息的接口;

NullClientInfoProviderIClientInfoProvider 接口的默认实现;

IAuditingStore:审计信息持久化接口,应由上层提供商实现;

SimpleLogAuditingStoreIAuditingStore 接口的默认实现;

入口

AbpBootstrapper 提供统一的拦截器注入入口:

QQ截图20190727150235.png

QQ截图20190727150303.png

AuditingInterceptorRegistrar:审计拦截器注册类,如下图:

QQ截图20190727150506.png

AuditingInterceptor:审计拦截器,提供对同步方法和异步方法的拦截操作,如下图:

QQ截图20190727152548.png

QQ截图20190727153054.png

QQ截图20190727153419.png

IAuditingHelper:记录审计信息帮助类;

AuditingHelperIAuditingHelper 接口的实现,提供 ShouldSaveAuditCreateAuditInfoSave等方法,如下图:

QQ截图20190727152907.png

IAuditingConfiguration:定义用于配置审计的接口;

AuditingConfigurationIAuditingConfiguration 的默认实现,如下图:

QQ截图20190727151604.png

QQ截图20190727153811.png

AbpKernelModule 的预初始化方法 PreInitialize 中调用,如下图:

QQ截图20190727151740.png

IAuditingSelectorList:用于选择要审计的类/接口的选择器函数列表。

AuditingSelectorListIAuditingSelectorList接口的默认实现;

NamedTypeSelector:类型选择器,这个对象的核心属性是一个以Type为输入参数,返回Bool类型的委托 Predicate;

QQ截图20190727153936.png

参考:

ABP源码分析十九:Auditing

ABP框架学习记录(13)- Entity解析

Entity 在ABP项目中的目录位置:

QQ截图20190722112057.png

Entity

IEntity<TPrimaryKey>:封装了PrimaryKey:Id,这是一个泛型类型,所有的实体都必须实现此接口;

IEntity: 继承 IEntity<int> 泛型接口;

Entity<TPrimaryKey>:继承 IEntity<TPrimaryKey> 接口的基础实现,支持主键是泛型的实体,该类重写了Equals,GetHashCode,ToString 方法和==,!= 运算符。

Entity:主键是int类型的Entity;

IEntityTranslation:实体语言转化;

IEntityTranslation<TEntity, TPrimaryKeyOfMultiLingualEntity>: 实体语言转换泛型类,TPrimaryKeyOfMultiLingualEntity 为主键类型;

IEntityTranslation<TEntity>:实体语言转换泛型类,继承 IEntityTranslation<TEntity, int> 接口;

IMultiLingualEntity<TTranslation> :多语言实体,其中 TTranslation 类型约束为 IEntityTranslation

IEntityTranslation<> 接口的实现,表明当前实体可以被翻译:

QQ截图20190722150243.png

IMultiLingualEntity<> 接口的实现,表示有多语言的实现:

QQ截图20190722150428.png

使用:

QQ截图20190722150703.png

QQ截图20190722150813.png

IExtendableObject:使用 Json 格式的字符串属性,以扩展对象/实体。

ExtendableObjectExtensions:针对扩展字段属性的扩展类;

IAggregateRoot:聚合根接口

AggregateRoot:聚合根实现

QQ截图20190722153654.png

QQ截图20190722153720.png

IMayHaveTenant:为可能选择具有TenantId的实体实现此接口。

IMustHaveTenant:为必须具有TenantId的实体实现此接口。

IPassivable:此接口用于使实体主动/被动。

ISoftDelete:用于标准化软删除实体。

EntityCache

IEntityCache<TCacheItem, TPrimaryKey>:定义实体缓存接口,此接口为泛型类型;

IEntityCache<TCacheItem>:继承 IEntityCache<TCacheItem, int> 接口;

EntityCache<TEntity, TCacheItem, TPrimaryKey>:实体缓存类;

QQ截图20190722171203.png

Auditing

IAudited:定义实体的审计接口,保存/更新时自动设置相关属性

IAudited<TUser>:审计接口。

ICreationAudited: 审计创建者接口,保存到数据库时,会自动设置创建时间和创建者用户。

IDeletionAudited: 删除时审计信息

IFullAudited:完全审核信息。

IHasCreationTime:如果必须存储实体,则可以实现此接口,自动设置;

IHasDeletionTime:如果必须删除实体,则可以实现此接口,自动设置;

IHasModificationTime:更新实体时间;

IModificationAudited:实体更新人信息;

AuditedEntity:审计实体;

CreationAuditedEntity:创建者审计实体;

FullAuditedEntity:全部审计信息实体;

AuditedAggregateRoot:审计聚合根

CreationAuditedAggregateRoot:创建者审计 聚合根;

FullAuditedAggregateRoot:全部审计信息 聚合根;

EntityAuditingHelper:实体审计帮助类;

QQ截图20190722174813.png

参考:

ABP源码分析十四:Entity的设计

CSharp - 基元类型(primitive)

基元类型

CLR 定义的基元(原始)类型:

Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double, Single

注意:string不是基元(原始)类型。

即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
System.Boolean
System.Byte
System.SByte
System.Int16
System.UInt16
System.Int32
System.UInt32
System.Int64
System.UInt64
System.IntPtr
System.UIntPtr
System.Char
System.Double
System.Single

通过 System.Type 提供 IsPrimitive 属性,指示当前类型是否为基元类型;

内置类型

C#内置类型和.NET框架中的类型有直接的映射关系。C#内置类型直接映射到Framework(FCL)中存在的类型。如: 在用内置类型int初始化一个整数时,int会直接映射到FCL中 System.Int32 类型,这个过程,编译器自动完成。C#中的string是内置类型,直接映射到.NET框架中的String,所以没有任何不同。

下图显示,C#中的所有内置类型和FCL类型:

QQ截图20190705175059.png

隐式转化:如果转化过程时安全的,也就是说在转化过程中数据不会丢失,那么久进行隐式转换。
显示转换:转换过程不安全,意味着可能会丢失精度或者数量级,那就要求显示转换。

参考:

C# 基元类型(primitive)

C#中基元类型、引用类型与值类型

内置类型表(C# 参考)

Is String a primitive type?

Type.IsPrimitiveImpl Method

简介:CLI是一个开放的技术规范。它是由微软联合惠普以及英特尔于2000年向ECMA倡议的。通用语言基础架构定义了构成.NET Framework基础结构的可执行码以及代码的运行时环境的规范,它定义了一个语言无关的跨体系结构的运行环境,这使得开发者可以用规范内定义的各种高级语言来开发软件,并且无需修正即可将软件运行在不同的计算机体系结构上。CLI有时候会和CLR混用。但严格意义上说,这是错误的。因为CLI是一种规范,而CLR则是对这种规范的一个实现。

首先,引入一张图片:

2010012620512419.png
图片引用自:https://www.cnblogs.com/eshizhan/archive/2010/01/26/1657041.html

流程图:

CLI-00001.png

CLI

CLI:通用语言基础架构(Common Language Infrastructure,CLI);

CLI是一个开放的技术规范。它是由微软联合惠普以及英特尔于2000年向ECMA倡议的。通用语言基础架构定义了构成.NET Framework基础结构的可执行码以及代码的运行时环境的规范,它定义了一个语言无关的跨体系结构的运行环境,这使得开发者可以用规范内定义的各种高级语言来开发软件,并且无需修正即可将软件运行在不同的计算机体系结构上。CLI有时候会和CLR混用。但严格意义上说,这是错误的。因为CLI是一种规范,而CLR则是对这种规范的一个实现。

欧洲计算机制造商协会(ECMA)已经于2001年10月13日批准C#语言规范(ECMA-334)成为一种新诞生的计算机产业标准。同时国际标准组织ISO也同意该标准进入该组织的审批阶段。并且,作为.NET与CLR的核心部分,CLI与C#也同时获得了ECMA的批准(ECMA-335)。拥有了C#与CLI这两项标准,你可以自己写出能够运行于任何操作系统上的.NET平台(只要你愿意)。如前所述,著名的MONO项目就是这么干的,MONO项目包括三个核心的部分:一个C#语言的编译器,一个CLI和一个类库。

CLR

CLR:通用语言运行平台(Common Language Runtime,CLR);

无论通过任何语言构建产品,都必须寄宿到一个平台中运行,这正如我们的软件运行在操作系统环境一样,操作系统为CLR提供了运行环境,使用.NET构建的程序又运行在CLR之上,CRL为.NET程序的运行提供了温床,CLR提供基本的类库和运行引擎,基本类库封装操作系统函数供开发者方便调用,运行引擎用于编译并运行我们开发的程序。CLR包含.NET运行引擎和符合CLI的类库。通过.NET平台构建的程序都基于CLR基础类库来实现,并且运行在CLR提供的运行引擎之上。

编译为托管代码时,编译器将源代码翻译为 Microsoft 中间语言 (MSIL),这是一组可以有效地转换为本机代码且独立于 CPU 的指令。MSIL 包括用于加载、存储和初始化对象以及对对象调用方法的指令,还包括用于算术和逻辑运算、控制流、直接内存访问、异常处理和其他操作的指令。要使代码可运行,必须先将 MSIL 转换为特定于 CPU 的代码,这通常是通过实时 (JIT) 编译器来完成的。由于公共语言运行库为它支持的每种计算机结构都提供了一种或多种 JIT 编译器,因此同一组 MSIL 可以在所支持的任何结构上 JIT 编译和运行。

CIL

CIL:公共中间语言(Common Intermediate Language,CIL);

CLI,简称微软中间语言(MSIL)或者中间语言(IL)。CIL是编译器将.NET代码编译成公共语言运行时(CLR)能够识别的中间代码。它是一种介于高级语言(例如C#)和CPU指令之间的一种语言。当用户编译一个.NET程序时,编译器(例如VisualStudio)将C#源代码编译转换成中间语言 (MSIL),它是一种能够被CLR转换成CPU指令的中间语言,当执行这些中间语言时,CLR提供的实时(JIT)编译器将它们转化为CPU特定的代码。由于公共语言运行库支持多种实时编译器,因此同一段中间语言代码可以被不同的编译器实时编译并运行在不同的CPU结构上。从理论上来说,MSIL将消除多年以来业界中不同语言之间的纷争。在.NET的世界中可能出现下面的情况一部分代码可以用C++实现,另一部分代码使用C#或VB.NET完成的,但是最后这些代码都将被转换为中间语言。这给程序员提供了极大的灵活性,程序员可以选择自己熟悉的语言,并且再也不用为学习不断推出的新语言而烦恼了。

FCL

FCL:框架类库(Framework Class Library,FCL);

FCL提供了大粒度的编程框架,它是针对不同应用设计的框架 ,FCL大部分实现都引用了BCL,例如我们常说的开发框架:ASP.NET、MVC、WCF和WPF等等,提供了针对不同层面的编程框架 。

FCL框架类库可以划分为三层:

最内一层:由BCL的大部分组成,主要作用是对.NET框架,.NET运行时以及CIL语言本身进行支持,例如基元类型,集合类型,线程处理,应用程序域,运行时,安全性,互操作等。
中间一层:包含了对操作系统功能的封装,例如文件系统,网络连接,图形图像,XML操作等。
最外一层:包含各种类型的应用程序,例如 Window Forms,Asp.NET,WPF,WCF,WF等。

BCL

BCL:基类库(Base Class Library,BCL);

BCL是一个公共编程框架,称为基类库,所有语言的开发者都能利用它。是CLI(Common Language Infrastructure,公共语言基础结构)的规范之一,主要包括:执行网络操作,执行I/O操作,安全管理,文本操作,数据库操作,XML操作,与事件日志交互,跟踪和一些诊断操作,使用非托管代码,创建与调用动态代码等,粒度相对较小,为所有框架提供基础支持。

CLS

CLS:公共语言规范(Common Language Specification,CLS);

CLS是CTS的一个子集,所有.NET语言都应该遵循此规则才能创建与其他语言可互操作的应用程序,但要注意的是为了使各语言可以互操作,只能使用CLS所列出的功能对象,这些功能统称为与CLS兼容的功能,它是在.NET平台上运行语言的最小规范,正因为.NET上不同语言能够轻松交互一样,例如C#编写程序时可以直接引用并使用VB.NET编写的类库。为了达到这样的交互,才制定出CLS规范,在.NET框架本身提供的所有类库(并非所有)都是与CLS兼容的,在查看MSDN文档时,不兼容的类和方法都被特别标记为不兼容,例如C#中的System.UInt32就标记为 此API不兼容CLS。兼容 CLS的替代API为 Int64。这说明并不是所有的语言(例如VB.NET或J#)都支持无符号的数据类型,但这种数据类型与CLS不兼容的。

CTS

CTS:公共类型系统(Common Type System,CTS);

CTS是一种类型系统和语言规范,它能够确保CLR能够识别和处理的类型,所有.NET开发语言中的类型,无论时VS.NET类型还是C#.NET类型最终都会被编译成CLR能够识别的CTS类型,因此CTS是.NET平台类型的抽象。例如VB.NET中的integer类型和C#中的int类型都编译成CTS的System.Int32类型。如果某种语言编写的程序能够在CLR上运行,并不能说明这种语言完全符合CTS的规范。例如使用C++编写的代码,部分代码并不符合CTS规范,在编译时把这部分不符合CTS的代码会被编译成原始代码本地CPU指令而非中间代码。

参考:

Microsoft Win32 to Microsoft .NET Framework API Map

Common Language Infrastructure

Overview of the Common Language Infrastructure.svg

BCL和FCL

关于CLR、CIL、CTS、CLS、CLI、BCL和FCL 的区分与总结

ABP框架学习记录(12)- Redis缓存

在 ABP 框架中,单独新建了一个项目来实现 Redis 缓存,具体项目/目录如下图:

QQ截图20190701143752.png

首先,AbpRedisCacheModule 模块用于使用Redis服务器取代ABP的缓存系统,AbpRedisCacheModule 依赖于 AbpKernelModule

QQ截图20190701175116.png

AbpRedisCacheOptions : 提供 AbpStartupConfiguration 属性,为 RedisCacheConfigurationExtensions 扩展类提供 IIocManager 的实例:
QQ截图20190701175312.png

提供 IIocManager 实例,并注册 AbpRedisCacheManager 类做为 ICacheManager 的默认实现:

QQ截图20190701180120.png

AbpRedisCacheManager : 用户管理 AbpRedisCache;

QQ截图20190701180537.png

IAbpRedisCacheDatabaseProvider/AbpRedisCacheDatabaseProvider :用户获取 IDatabase 实例;

QQ截图20190701181130.png

IRedisCacheSerializer/DefaultRedisCacheSerializer:持久化和检索时使用的所有自定义(反)序列化方法;

QQ截图20190701181427.png

RedisDatabaseExtensions:扩展类;

QQ截图20190701181522.png

ABP框架学习记录(11)- 缓存的实现

缓存在 ABP 项目中的路径 ABP/Runtime

QQ截图20190628105843.png

ABP中实现两种 CacheMemroyCacheRedisCache。两者都继承至 ICache 接口(准确说是 CacheBase 抽象类)。ABP核心模块封装了 MemroyCache 来实现ABP中的默认缓存功能。 Abp.RedisCache 这个模块封装 RedisCache 来实现缓存(通过 StackExchange.Redis 这个类库访问redis)

ICache 接口:定义可以按键存储和获取项目的缓存。

QQ截图20190628151914.png

ICache 接口中的方法 Get/GetAsync 有两个参数:
key:缓存关键字,字符串类型;
工厂:没有找到指定key的缓存条目时调用传入的action来创建cache。工厂方法应该创建并返回实际的条目。如果给定的key在缓存中找到了,那么不会调用该action。

QQ截图20190628152326.png

CacheBase:提供继承 ICache 接口的抽象类;

QQ截图20190701132308.png

CacheExtensions:提供 ICache的扩展类:

QQ截图20190701132333.png

ICacheManager/CacheManagerBase:提供缓存管理器;

QQ截图20190628162728.png

ICacheManager.GetCache 方法返回一个ICache。第一次请求时会创建缓存,并通过 CachingConfiguration 中的CacheConfigurator 完成对该Cache的配置,以后都是返回相同的缓存对象。因此,我们可以在不同的类(客户端)中共享具有相同名字的相同缓存。

QQ截图20190628162758.png

ICachingConfiguration/CachingConfiguration:用于配置 缓存系统,首先通过在 AbpKernelModule 类,PreInitialize 方法配置缓存:

QQ截图20190701142545.png

定义:

QQ截图20190701141638.png

QQ截图20190701141747.png

ICacheConfigurator/CacheConfigurator:表示已注册的缓存配置器,定义两个构造函数,其中的参数 Action<ICache> initAction 表示创建的回调函数;

QQ截图20190701142143.png

AbpKernelModule 类,PreInitialize 方法调用:

QQ截图20190701142545.png

CacheManagerExtensions:提供对 CacheManager 扩展类,提供 GetCache<TKey, TValue> 方法,具体实现调用 CacheExtensionsAsTyped<TKey, TValue> 扩展方法;

QQ截图20190701132812.png

QQ截图20190701133029.png

AsTyped<TKey, TValue> 扩展方法,实例化了一个 TypedCacheWrapper<TKey, TValue>(cache) 类型的对象,并将 CacheManagerExtensions 中扩展方法 GetCache<TKey, TValue>TKey, TValue,然后继续传递给 CacheExtensions 中的扩展方法 AsTyped<TKey, TValue>TKey, TValue,最终,在 TypedCacheWrapper 的泛型类中接收,并为其自身的方法提供 TKey, TValue

QQ截图20190701133134.png

ITypedCache/TypedCacheWrapper/TypedCacheExtensions:支持泛型key和value的缓存接口与实现,其内部通过封装ICache实例和CacheExtension定义的对ICache的扩展方法来是实现泛型版本的 Icache

QQ截图20190628153126.png

QQ截图20190628153232.png

AbpMemoryCacheManager :重写了 CacheManagerBaseCreateCacheImplementation 方法,该方法用于创建真实的 Icache 对象。 具体到 AbpMemoryCacheManager 就是创建 AbpMemoryCache

QQ截图20190701143053.png

AbpMemoryCache:通过CLR的 MemoryCache 来实现 Icache

QQ截图20190701143151.png

使用

QQ截图20190701172446.png

参考:

ABP源码分析十三:缓存Cache实现

ABP框架学习记录(10)- 本地化

ABP中的本地化主要涉及两个方面:一个是语言(Language)的管理(相对简单),另一个是语言对应得本地化资源(Localization)的管理(稍显复杂)。

目录结构:

QQ截图20190618095421.png

引用

AbpKernelModule 完成对本地化资源的引用,设置的初始化:

PreInitialize 方法调用 AddLocalizationSources 方法,添加资源:

QQ截图20190618131540.png

PreInitialize 方法调用 AddSettingProviders 方法,添加设置:

QQ截图20190618111856.png

PostInitialize 方法初始化:

QQ截图20190618111740.png

LocalizationManager/ILocalizationManager

遍历 LocalizationConfiguration 中的 ILocalizationSourceList 实例,通过 ILocalizationSourceILocalizationDictionaryProvider 实例完成本地化资源的初始化。

QQ截图20190618135216.png

ILocalizationSource 接口:

QQ截图20190618140712.png

ILocalizationConfiguration/LocalizationConfiguration: 用于配置支持本地化的语言的一个 LanguageInfo 集合,以及这些语言所对应的本地化资源。这两者分别对应 ILocalizationConfiguration 中的 LanugagesSources 属性。注意这个Sources是一个 ILocalizationSourceList 实例。

DictionaryBasedLocalizationSource

IDictionaryBasedLocalizationSource : 继承 ILocalizationSource 接口,DictionaryBasedLocalizationSource 实现 IDictionaryBasedLocalizationSource 接口,用于构建本地化源,适用于基于内存的词典以查找字符串。

QQ截图20190618140440.png

子类的实现:

QQ截图20190618142054.png

应用:

QQ截图20190618142251.png

LocalizationSourceExtensionInfo:用于扩展本地化资源。ABP在 LocalizationManager 初始化的过程中将LocalizationSourceExtensionInfo 所对应的本地化资源扩充到 ILocalizationSource 对象的相应本地化资源字典中。

LocalizationDictionaryProviderBase

LocalizationDictionaryProviderBase 实现 ILocalizationDictionaryProvider 接口,它封装了一个IDictionary<string, ILocalizationDictionary> 实例(这是ABP在runtime时候,唯一持有本地化资源的对象),其中key就是sourceName。并且提供了一个方法Initialize来初始化本地化这个Dictionary。可以通过实现这个接口来提供其他类型的本地化资源。比如 Abp.Zero 就实现了数据库的本地化资源。

QQ截图20190618143125.png

Initialize 方法:

QQ截图20190618143206.png

LocalizationDictionaryProviderBase:实现了 ILocalizationDictionaryProvider 的抽象类,实现了extend本地化Dictionary的方法,这个方法主要用于初始化完成以后,用于扩展相应的ILocalizationDictionary对象。

XmlFileLocalizationDictionaryProviderJsonFileLocalizationDictionaryProviderJsonEmbeddedFileLocalizationDictionaryProviderXmlEmbeddedFileLocalizationDictionaryProvider 类,继承 LocalizationDictionaryProviderBase;

LocalizationDictionary

ILocalizationDictionary:提供了索引器this[]方法的接口,该方法接受一个string返回的是本地化的string。当LocalizationManager 初始化动作结束后,每一种本地化语言的都对应有且仅有的一个 ILocalizationDictionary 对象,这个对象用于保存该语言的所有本地化信息。

LocalizationDictionary:实现了 ILocalizationDictionaryIEnumerable 两个接口,他本身就是一个具有集合操作的类。其内部封装了一个Dictionary的实例,用于提供真正的集合操作。这个基类只提供了从其内部的Dictionary中根据原string查找返回本地化的string。 其本身并没有将本地化资源文件中的数据加载到其内部的Dictionary的功能,这部分是在其子类中实现的。

QQ截图20190618143919.png

XmlLocalizationDictionary:实现 BuildFomFileBuildFomXmlString 方法用于从XML文件读取本地化数据。

JsonLocalizationDictionary:实现 BuildFromFileBuildFromJsonString 方法用于从Json文件读取本地化数据。

JsonLocalizationFile: 反序列化Json字符串到 JsonLocalizationFile 对象。

LanguageManager

ILanguageManager/LanguageManager:通过调用 ILanguageProvider 接口返回 LanguageInfo 的一个集合。以及返回服务器的当前语言设置,如果服务器的当前语言不在 LocalizationConfiguration 的本地化语言集合中,则返回LocalizationConfiguration 的本地化语言集合中的第一项。

ILanguageProvider:接口定义一个返回本地化语言集合的方法。这里使用接口做隔离是有必要的,因为ABP底层框架的DefaultLanguageProvider 只是返回通过代码 hardcode (硬编码) 到系统中的 LanguageInfo 信息。如果需要从其他source(比如数据库)中获取配置的 LanguageInfo 信息,那么我们就必须实现自定义的 LanguageProvider

DefaultLanguageProvider:从 LocalizationConfiguration 读取 LanguageInfo 的集合。

LanguageInfo:用于封装language的基本信息。

参考:

ABP源码分析十二:本地化

使用Json.Net序列化上传的文件

基本知识:

序列化: 将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

本文提供一种针对目标文件,重写文件序列化,从而实现上传。

客户端调用:

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
public void Send()
{
var inFile1 =
@"C:\Users\User1\source\repos\ConsoleApp1\Test.mp3";
byte[] data = File.ReadAllBytes(inFile1);

FileUpLoadDTO gdto = new FileUpLoadDTO();

// 其它参数
gdto.DeviceId = "1234321";
gdto.DeviceMac = "1343212";

var filedto = new FileDTO();
filedto.FileData = data;
filedto.FileName = GetFileName();
filedto.FileSize = data.Length;
gdto.FileDto = filedto;

// 序列化文件
var jsonData = JsonHelper.Serialize(new { dto = gdto });
var url = "http://temp.uri/UpLoadFile";
var response = HttpHelper.SendRequest(url, "POST", jsonData);
}

public string GetFileName()
{
Random rd = new Random();
StringBuilder serial = new StringBuilder();
serial.Append(DateTime.Now.ToString("yyyyMMddHHmmssff"));
serial.Append(rd.Next(0, 999999).ToString());
return serial.ToString();
}

服务端实现:

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
public ReturnInfoDTO ReceiveFile(FileUpLoadDTO dto)
{
ReturnInfoDTO rdto = new ReturnInfoDTO() { StatusCode = "200", IsSuccess = true, Message = "调用成功" };

var outputFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NAudio");
Directory.CreateDirectory(outputFolder);

var filename = dto.FileDto.FileName;

try
{
using (FileStream stream = File.Open(filename, FileMode.OpenOrCreate))
{
stream.Seek(0, SeekOrigin.End);
stream.Write(dto.FileDto.FileData, 0, dto.FileDto.FileData.Length);
}
}
catch (Exception e)
{
rdto.IsSuccess = false;
rdto.StatusCode = "500";
rdto.Message = "调用失败";
}

return rdto;
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void button9_Click(object sender, EventArgs e)
{
var inFile1 =
@"C:\Users\User1\Desktop\Test.mp3";
byte[] data = File.ReadAllBytes(inFile1);

FileUpLoadDTO gdto = new FileUpLoadDTO();

// 其它参数
gdto.DeviceId = "1234321";
gdto.DeviceMac = "1343212";

var filedto = new FileDto();
filedto.FileData = data;
filedto.FileName = GetFileName() + System.IO.Path.GetExtension(inFile1);
filedto.FileSize = data.Length;
gdto.FileDto = filedto;

ReceiveFile(gdto);
}

JsonHelper帮助类:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class JsonHelper
{
public static T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
}

public static string Serialize<T>(T data)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
ByteJsonConverter byteJsonConverter = new ByteJsonConverter();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
DateTimeConverter dateTimeConverter = new DateTimeConverter();
settings.Converters.Add(dateTimeConverter);
settings.Converters.Add(byteJsonConverter);
return JsonConvert.SerializeObject((object)data, settings);
}

internal static string ObjectToJson<T>(T obj)
{
using (MemoryStream memoryStream = new MemoryStream())
{
new DataContractJsonSerializer(typeof(T)).WriteObject(memoryStream, obj);
memoryStream.Position = 0L;
using (StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8))
return streamReader.ReadToEnd();
}
}
}

internal class DateTimeConverter : JsonConverter
{
public override bool CanRead
{
get { return false; }
}

public override bool CanWrite
{
get { return true; }
}

public override bool CanConvert(Type objectType)
{
return typeof(DateTime) == objectType || typeof(DateTime?) == objectType;
}

public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string json = JsonHelper.ObjectToJson<DateTime>(Convert.ToDateTime(value));
writer.WriteRawValue(json);
}
}

internal class ByteJsonConverter : JsonConverter
{
public override bool CanRead
{
get { return false; }
}

public override bool CanWrite
{
get { return true; }
}

public override bool CanConvert(Type objectType)
{
return typeof(byte[]) == objectType;
}

public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string json = JsonHelper.ObjectToJson<byte[]>(value as byte[]);
writer.WriteRawValue(json);
}
}

参考:

system.io.filestream

Newtonsoft.Json

使用NAudio将MP3编码转G711

将MP3编码转成G711编码,以支持终端播放需求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var inFile1 =
@"C:\Users\test\source\repos\ConsoleApp1\7794FDBE-34E9-4218-B5EC-2272ED750201.mp3";
byte[] data = File.ReadAllBytes(inFile1);

var outputFolder = Path.Combine(System.Environment.CurrentDirectory, "NAudio");
Directory.CreateDirectory(outputFolder);

var filename = GetFileName();
string g711FileName = filename + "-g711.wav";

var inFile = Path.Combine(outputFolder, filename + ".wav");
var outfile = Path.Combine(outputFolder, g711FileName);

using (MemoryStream ms = new MemoryStream(data))
using (var reader = new Mp3FileReader(ms))
{
WaveFileWriter.CreateWaveFile(inFile, reader);
}

NAudioHelper.EnCodeG711(inFile, outfile);
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/// <summary>
/// Naudio 转码
/// </summary>
public class NAudioHelper
{
private static MuLawChatCodec _codec = new MuLawChatCodec();
public static void EnCodeG711(string inFile, string outfile)
{
var format = new WaveFormat(8000, 16, 1);
using (var reader = new WaveFileReader(inFile))
using (WaveFileWriter waveFileWriter = new WaveFileWriter(outfile, format))
{
byte[] buffer = new byte[reader.WaveFormat.AverageBytesPerSecond * 4];
while (true)
{
int count = reader.Read(buffer, 0, buffer.Length);

byte[] encoded = _codec.Encode(buffer, 0, count);

if (count != 0)
waveFileWriter.Write(encoded, 0, encoded.Length);
else
break;
}
}
}
public static string GetFileName()
{
Random rd = new Random();
StringBuilder serial = new StringBuilder();
serial.Append(DateTime.Now.ToString("yyyyMMddHHmmssff"));
serial.Append(rd.Next(0, 999999).ToString());
return serial.ToString();
}
}

internal class MuLawChatCodec
{
public string Name => "G.711 mu-law";

public int BitsPerSecond => RecordFormat.SampleRate * 8;

public WaveFormat RecordFormat => new WaveFormat(8000, 16, 1);

public byte[] Encode(byte[] data, int offset, int length)
{
var encoded = new byte[length / 2];
int outIndex = 0;
for (int n = 0; n < length; n += 2)
{
encoded[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(data, offset + n));
}

return encoded;
}

public byte[] Decode(byte[] data, int offset, int length)
{
var decoded = new byte[length * 2];
int outIndex = 0;
for (int n = 0; n < length; n++)
{
short decodedSample = MuLawDecoder.MuLawToLinearSample(data[n + offset]);
decoded[outIndex++] = (byte)(decodedSample & 0xFF);
decoded[outIndex++] = (byte)(decodedSample >> 8);
}

return decoded;
}

public void Dispose()
{
// nothing to do
}

public bool IsAvailable
{
get { return true; }
}
}