C# programming guide
- Programming concepts
- Attributes
- [Overview]
- Creating Custom Attributes
- 可以从System.Attribute直接或者间接派生出一个标类来创建新属性
- 可以用System.AttributeUsage修饰属性,比如要运行设置多次同一属性的话,要把AllowMultiple设置为亍值。
- 指定一个属性其实相当于是构造一个属性类,并设置其成员的值
- 可以从System.Attribute直接或者间接派生出一个标类来创建新属性
- Accessing Attributes by Using Reflection
- GetCustomAttributes可以获取属性对象(导致动态创建属性对象)
- 也可以用GetCustomAttributesData
- GetCustomAttributes可以获取属性对象(导致动态创建属性对象)
- How to create a C/C++ union by using attributes
- 可以使用System.Runtime.InteropServices.StructLayout属性来控制内存布局
- Reflection
- 勘察可以发现装配件、模块以及类型相关的信息,可以用勘察来
- 动态创建某类型的现例
- 将类型绑定到已有现例
- 从现例获取类型,并访问类型中指定的方法、域属、辖属
- 访问代码中的属性
- GetType可以用来获取类型
- 例子:
Type type = i.GetType();
,示例输出System.Int32
- 例子:
- 获取装配件信息
- 例子:
Assembly info = typeof(int).Assembly;
- 例子:
- C#中的关键字protected和internal在IL中无意义,在勘察用编口中不做使用
- IL中对应的术语叫做Family和Assembly
- internal使用IsAssembly来对应
- protected internal使用IsFamilyOrAssembly来对应
-
Reflection overview
- 勘察的用途
- 访问程序元数据中的属性
- 检查和例现化装配件中的类型
- 在运行时通过System.Reflection.Emit来构建新类型
- 执行晚绑定,在运行时访问类型上创建的方法,参考https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/dynamically-loading-and-using-types
- 勘察的用途
-
Related sections
- 指到了其他地方的文档……
- 勘察可以发现装配件、模块以及类型相关的信息,可以用勘察来
- Attributes
Language-Integrated Query (LINQ)
- Overview
- 对SQL、XML等数据源的查询一半有专门的语言或者方式,以字符串的形式体现在编程语言中,然后在程序语言运行时才解析执行。这带来的问题是,错误无法在程序语言编译时就发现,影响使用体验。
- LINQ在程序语言层面提供了一系列技术,让查询可以通过程序语言的结构来构建,可以增加编译时的检查。
- LINQ技术包含但不限于
- LINQ to objects
- LINQ to SQL
- LINQ to XML
- 开发者最经常处理的时LINQ表达式,由声明性的措辞写就。可以对数据源进行一系列操作。
- IEnumerable合集例子:
// Specify the data source. int[] scores = { 97, 92, 81, 60 }; // Define the query expression. IEnumerable<int> scoreQuery = from score in scores where score > 80 select score;
- 查询表达式概览
- 单个查询表达式可以针对多个数据源
- 查询表达式会检查类型
- 查询表达式在使用时方执行
- 编译时,根据C#规范,查询表达式被转化为Standard Query Operator方法调用
- 也就是说,能用查询表达式,可以被方法调用完全表达,反之则不然
- 查询表达式可以编译为表达式树体,或者递执体
IEnumerable<T>
查询被编译成递执体IQueryable<T>
查询则被编译查表达式树体
- Getting Started with LINQ in C#
-
Introduction to LINQ Queries (C#)
- Three Parts of a Query Operation
- 所有的LINQ查询操作都分为三个不同的动作:
- 获取数据源
- 创建查询
- 执行查询
- The Data Source
- LINQ需要LINQ provider的支持。
- The Query
- 查询表达式初始化一个查询变量。
- Query Execution
- Deferred Execution
- 默认是延迟执行的。
- 查询变量自身不持有查询结果,因此可以执行多次。
- Forcing Immediate Execution
- 查询中的聚合函数,比如Counter、Max、Average、First等,需要即时执行。
- 可调用ToList或者ToArray方法来即时执行查询。
- Deferred Execution
-
- LINQ查询基于通用类型,是在.NET Framework 2.0引入的。有一些基本概念需要理解:
- C++中的通用类型相似,参考https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics
IEnumerable<T>
让foreach可以枚举合集IQueryable<T>
派生自IEnumerable<T>
- IEnumerable variables in LINQ Queries
- 如果查询变量类型为
IEnumerable<Customer>
,意味着查询结果是零至多个Customer目件。
- 如果查询变量类型为
- Letting the Compiler Handle Generic Type Declarations
- 使用var关键字可以让编译器自动推断查询变量的类型。但是也会造成代码意图不明显的问题,参考https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/implicitly-typed-local-variables。
-
Basic LINQ Query Operations (C#)
- 列举了集中LINQ常见操作。
-
Data Transformations with LINQ (C#)
- LINQ不仅可以获取数据,还可以对数据进行变换。
- LINQ可以通过select句段创建新的类型:
- 合并多个输入序列,到一个新类型的输出序列
- 创建一个输出序列,其元素只包含原始序列中元素的个别辖属
- 创建一个输出序列,其元素是经过处理的原始序列元素
- 以不同的格式创建输出序列
- Joining Multiple Inputs into One Output Sequence
- 一个例子。
- Selecting a Subset of each Source Element
- 居然有匿名类型这种东西,参考https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/anonymous-types,可实习:
var query = from cust in Customer select new {Name = cust.Name, City = cust.City};
�考<https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers>
- 居然有匿名类型这种东西,参考https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/anonymous-types,可实习:
- Transforming in-Memory Objects into XML
- 可以直接从select创建对象,比如:
var studentsToXML = new XElement("Root", from student in students let scores = string.Join(",", student.Scores) select new XElement("student", new XElement("First", student.First), new XElement("Last", student.Last), new XElement("Scores", scores) ) // end "student" ); // end "Root"
- 可以直接从select创建对象,比如:
- Performing Operations on Source Elements
- 可以直接在查询结果上执行运算,比如下面的:
IEnumerable<string> output = radii.Select(r => $"Area for a circle with a radius of '{r}' = {r * r * Math.PI:F2}");
�考<https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated>
- 可以直接在查询结果上执行运算,比如下面的:
-
Type Relationships in LINQ Query Operations (C#)
- Queries that do not Transform the Source Data 过。
- Queries that Transform the Source Data 过。
- Letting the compiler infer type information 过。
-
Query Syntax and Method Syntax in LINQ (C#)
-
标准的Linq查询操作在System.Linq命名空间下。
-
Standard Query Operator Extension Methods
- 采用方法措辞:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
�要注意的是,这些保准查询操作时采用extension methods的方式实现的,参考<https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods>。
- 采用方法措辞:
-
Lambda Expressions 过。
-
Composability of Queries 略。
-
-
- Query Expressions
- 措辞与SQL或XQuery相似。 参考https://learn.microsoft.com/en-us/dotnet/csharp/linq/。
- Implicitly Typed Variables (var)
- Object and Collection Initializers
- 具有构造目件的方便语法,不必使用构造函数,如下所示:
var cust = new Customer { Name = "Mike", Phone = "555-1212" };
�考<https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers>以及<https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/query-expression-syntax-for-standard-query-operators>。
- 具有构造目件的方便语法,不必使用构造函数,如下所示:
- Anonymous Types
- 编译器能够即时生成类型,这种即时类型只有编译器自己能访问。
select new {name = cust.Name, phone = cust.Phone};
- 编译器能够即时生成类型,这种即时类型只有编译器自己能访问。
- Extension Methods
- Lambda Expressions
- Query Expressions
-
Walkthrough: Writing Queries in C# (LINQ)
- 给了一个例子,但是数据源只是驻内存的目件合集。
-
- Standard Query Operators Overview (C#)
- Overview
- 文首
- 标准查询操作是形成LINQ模式的方法。此类方法大豆操作于序列,要么是
IEnumerable<T>
,要么是IQueryable<T>
。 - 是Enumerable和Queryable标类的静态方法,定义为扩展方法。 - 标准查询操作包括过滤、投射、聚合、排序等等
- 标准查询操作的执行时机也不相同 - 返回单例现的立马执行 - 返回序列的延迟执行
- 对于查询方法的呼召可以链接成一个查询
- 文中给了一个例子
- 标准查询操作是形成LINQ模式的方法。此类方法大豆操作于序列,要么是
- Query Expression Syntax
- 常用的标准查询方法,会以C#或者VB语言关键字的形式出现在qeury expression中
- Extending the Standard Query Operators
- 可以增强或者替换既有标准查询操作集合,参考https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.asenumerable
- Related Sections(略)
- Query Expression Syntax for Standard Query Operators
- query expression在编译时转化为方法调用
- Query Expression Syntax Table
- Cast,
from int i in numers
- GroupBy,
group...by
- GroupJoin,
join...in...on...equals...into...
- Join,
join...in...on...equals...
- OrderBy,
orderby
- OrderByDesending,
orderby ... descending
- Select,
select
- SelectMany, 多个
from
句段 - ThenBy,
orderby ..., ...
- ThenByDescending,
orderby ..., ... descending
- Where,
where
- Cast,
- Classification of Standard Query Operators by Manner of Execution
- 文首 - 执行分类:立即或延迟 - 延迟分类:流式和非流式
- Manners of Execution
- Immediate
- 返回标量结果的查询
- 使用 Enumerable.ToList 或 Enumerable.ToArray可以强制立即执行 - Deferred
- 通常返回的是
IEnumerable<T>
或IOrderedEnumerable<TElement>
- Streaming - Non-Streaming
- Classification Table
- 给出了一张表,说明每个操作的返回类型,以及相关特性
- Sorting Data
- 可以分两步排序
- Methods
- OrderBy,按照升序排列,对应
orderby
关键子 - OrderByDescending
- ThenBy,二次排序
- ThenByDescending
- Reverse,没有对应的C#关键字
- OrderBy,按照升序排列,对应
- Query Expression Syntax Examples
- Join Operations
- 有Join以及GroupJoin两个方法,按照键项来匹配两数据源,叫做equijoins
- 关系型数据库中,Join对应内连结,GroupJoin没有对应
-
Methods(过)
-
Query expression syntax examples(略)
- 有Join以及GroupJoin两个方法,按照键项来匹配两数据源,叫做equijoins
- 文首
- Overview
- LINQ to Objects (C#)
- 可以要单独列一篇记录。
- LINQ to ADO.NET (Portal Page)
- LINQ to ADO.NET意味着ADO.NET中支持的枚举,可以用LINQ来查询。
- LINQ to ADO.NET分三种技术。
- LINQ to DataSet DataSet是ADO.NET的常用组件,但是其查询能力有限。LINQ可以提供其查询能力,参考https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/linq-to-dataset
- LINQ to SQL
- LINQ to SQL可以将LINQ措辞编译成SQL,然后送给背后的数据库执行。待结果返回,将其转化为C#目件。
- LINQ to SQL支持存储过程,以及用户定义的函数。
- 更多参考https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/。
- LINQ to Entities
- Entity Data Model将关系型数据展示成.NET环境中的目件,自然适用于LINQ操作。
- 参考https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities。
- Enabling a Data Source for LINQ Querying
- 要让数据源支持LINQ,有以下条件:
- 实现
IEnumerable<T>
接口,以支持查询 - 创建标准查询操作对应的方法,例如Where,Select等
- 实现
IQueryable<T>
接口,以接收表达式树体形式的LINQ查询,来让LINQ可以以自定义的方式执行,比如在远程执行 - 让数据源适配已有的LINQ技术,这样不仅可以查询,还可以支持插入、更新以及删除操作
- 实现
- How to Enable LINQ Querying of Your Data Source
- 对于驻内存的数据,如果实现了
IEnumerable<T>
,就可以用LINQ to Objects查询。 或者实现LINQ标准查询操作方法,但注意要延迟执行。 - 对于远端的数据,实现
IQueryable<T>
比较好。
- 对于驻内存的数据,如果实现了
- IQueryable LINQ Providers
- LINQ提供方对
IQueryable<T>
的实现可能有不同的复杂度。 - 高复杂度的比如LINQ to SQL,需要在LINQ和SQL之间对应。
- 中复杂度的LINQ提供方需要做两两的类型映射。
- 低复杂度的LINQ往往对应于某些特定的查询。
- LINQ提供方对
- 要让数据源支持LINQ,有以下条件:
- Visual Studio IDE and Tools Support for LINQ (C#)
- SQLMetal是一个命令行工具,用于在构建过程中从现有数据库中生成标类,共LINQ to SQL使用,参考https://learn.microsoft.com/en-us/dotnet/framework/tools/sqlmetal-exe-code-generation-tool。
- C#编辑器对LINQ提供额外的支持。 -VS debugger对查询表达式提供额外支持,参考https://learn.microsoft.com/en-us/visualstudio/debugger/debugging-linq。
Classes, Structs, and Records
- Properties
- Properties overview
- 提供一种灵活的机制,用以读写以及计算私有域属的量值
- 辖属看似域属,但实际上是特殊的方法,叫做accessors
-
Properties overview
- 可以这么安排,域属是公开的,对应的实现是私有的
- 支持get、set方法,C# 9以后支持init;这些方法可以有不同的访问级别
- set、init中可以使用value关键字
- 可以是读写的、只读的、只写的(少见)
-
Properties with backing fields
- 可以用一个域属做背后支撑
-
Expression body definitions
- 支持下面的形式
public string Name => $"{_firstName} {_lastName}";
-
public decimal Price { get => _cost; set => _cost = value; }
- 支持下面的形式
-
Auto-implemented properties
- 自动生成实现,形如
public decimal Price { get; set; }
- 也支持get和init
- 可以有不同的访问等级
- 自动生成实现,形如
-
Required properties
- C# 11,可以使用required来强制要求客户端来初始化
- 例如
public required string Name { get; set; }
- 客户端代码
var item = new SaleItem { Name = "Shoes"};
-
C# Language Specification
- Properties overview
- Methods
- Extension Methods
- 文首
- 可以往既有类型添加方法,无须创建新派生类型,或重新编译等。
- 调用扩展方法和调用自身方法无明显区别
- 最常见的扩展方法是往System.Collections.IEnumerable和
System.Collections.Generic.IEnumerable<T>
上添加的LINQ查询操作符
- OrderBy Example
- 扩展方法必须是静态的,其所属的标类也必须是静态的
- 扩展方法无法访问私有成员
- 使用扩展方法,只需打开其标类所在的命名空间(或许还要引用到其装配件)
- 一个string扩展方法的例子
public static int WordCount(this string str)
,注意到this
的使用
- Binding Extension Methods at Compile Time
- 扩展方法无法覆盖标类的既有方法
- Example
- 此实例中,静态标类Extensions含有为接口IMyInterface实现的扩展方法
- Common Usage Patterns
- Collection Functionality
- Layer-Specific Functionality
- Extending Predefined Types
- General Guidelines
- 总之,一些注意事项
- 如果目的标类有同名方法,那么扩展方法不会被调用
- 扩展方法通过命名空间导入
- 扩展方法最好不要用于控制Assembly Versioning
- 总之,一些注意事项
- 文首
- How to implement and call a custom extension method
- To define and call the extension method
- Example(略)
- .NET Security(无碍)
- How to create a new method for an enumeration
- Example
- Extension Methods
(本篇完)