内容:

第四章(下): Advanced C#

  1. Tuple(C# 7.0)
  2. Atrribute
  3. Caller Info Attributes
  4. Dynamic Binding
  5. 运算符重载
  6. Unsafe code and Pointer
  7. Preprocessor Directives
  8. XML Documentation

1. Tuples (C# 7)

Tuples的主要作用是提供一种类型安全的,能让一个方法返回多个值而不是用out参数 的机制。

var bob = ("Bob", 23);   //创建了一个无名tuple,让compiler来推断类型,通过Item1,Item2来访问
Console.WriteLine (bob.Item1); // Bob
Console.WriteLine (bob.Item2); // 23

C# 7的tuple功能依赖于一系列的 generic structs ,叫做 System.ValueTuple<> , 他们并不是 .NET 4.6框架的一部分,而是包含在程序集System.ValueTuple中,需要使用Nuget来添加使用。

a. Tuples是值类型, 但是它的内容是可读可写的 :

var joe = bob; // joe is a *copy* of job
joe.Item1 = "Joe"; // Change joe's Item1 from Bob to Joe
Console.WriteLine (bob); // (Bob, 23)
Console.WriteLine (joe); // (Joe, 23)

b.明确指定tuple的类型:

(string,int) bob = ("Bob", 23); // var is not compulsory with tuples!static (string,int) GetPerson() => ("Bob", 23);

c. 泛型使用tuple:

Task<(string,int)>
Dictionary<(string,int),Uri>
IEnumerable<(int ID, string Name)> // See below for naming elements

1.1 命名Tuple

var tuple = (Name:"Bob", Age:23);
Console.WriteLine (tuple.Name); // Bob
Console.WriteLine (tuple.Age); // 23// 函数返回中指定名字
static (string Name, int Age) GetPerson() => ("Bob", 23);
static void Main()
{var person = GetPerson();Console.WriteLine (person.Name); // BobConsole.WriteLine (person.Age); // 23
}

a. tuple的元素类型一致时,是可以兼容的

(string Name, int Age, char Sex) bob1 = ("Bob", 23, 'M');
(string Age, int Sex, char Name) bob2 = bob1; // No error!//这容易造成使用误解
Console.WriteLine (bob2.Name); // M
Console.WriteLine (bob2.Age); // Bob
Console.WriteLine (bob2.Sex); // 23

b. 类型擦除:

之前提到过,C#编译器在处理匿名类型的时候,是把他编译成一个类,然后其中的property就是他们的字段。而C#对待Tuple则不是这样,它利用的是已经存在的一系列的结构体

public struct ValueTuple<T1>
public struct ValueTuple<T1,T2>
public struct ValueTuple<T1,T2,T3>
...

所以比如 (string, int)这个tuple就相当于是一种 ValueTuple<string, int>。而对于命名tuple,在底层实际是没有对应类型的, name的这些属性实际上只存在于 源代码中,是编译器的一种魔术,编译器编译之后,names大多数情况下就消失了,去查看编译后的结果,命名tuple在访问元素时,还是使用的 Item1,Item2...

所以在大多数情况下,无法使用反射来在运行时获取tuple的名称。

We said that the names mostly disappear, because there’s an exception. With methods/properties that return named tuple types, the compiler emits the element names by applying a custom attribute called TupleElementNamesAttribute (see “Attributes” on page 186) to the member’s return type. This allows named elements to work when calling methods in a different assembly (for which the compiler does not have the source code).

1.2 ValueTuple.Create

ValueTuple<string,int> bob1 = ValueTuple.Create ("Bob", 23);
(string,int) bob2 = ValueTuple.Create ("Bob", 23);

1.3 Deconstructing Tuples

Tuple默认就支持 Deconstruction

var bob = ("Bob", 23);
(string name, int age) = bob; // Deconstruct the bob tuple into

命名tuple之间的语法差异:

(string name, int age) = bob; // Deconstructing a tuple
(string name, int age) bob2 = bob; // Declaring a new tuple

1.4 相等性比较

就像匿名类型一样, ValueTuple<> 也重写了Equals方法,所以可以用来比较,也可以用来当做字典的Key:

var t1 = ("one", 1);
var t2 = ("one", 1);
Console.WriteLine (t1.Equals (t2)); // True
Console.WriteLine(t1 == t2);  //编译错误,C# 7.0 中不支持功能“元组相等”。请使用 7.3 或更高的语言版本。

1.5 The System.Tuple Classes

System名称空间中有一个类叫做Tuple,这是在 .NET 4.0引入的类,它是一个class,而不是一个 struct。 对tuple的使用来说,struct在性能上比class好,而且几乎没有缺点,所以微软在添加新的 Tuple特性的时候,直接加入了ValueTuple,而忽略了以前Tuple类。

Tuple<string,int> t = Tuple.Create ("Bob", 23); // Factory method
Console.WriteLine (t.Item1); // Bob
Console.WriteLine (t.Item2); // 23

2. Attributes

Attributes是一种对提供 自定义信息的扩展机制。当需要和 类型系统 有很强关联关系的时候,非常有用。一个非常好的Attribute的例子就是Serialization, 它能指定每个字段如何格式化来 Serialize。 如何自定义Attribute在Chap19讲。

2.1 Attribute Classes

一个Attribute定义成抽象类System.Attribute的实现类。惯例上所以Attribute类型都以XXXAttribute来命名。C#会识别这些命名方式,然后让你可以省去Attribute这个名字部分:

[ObsoleteAttribute]
public class Foo {...}
//相当于
[Obsolete]
public class Foo {...}

2.2 Named and Positional Attribute Parameters

Attribute使用的时候可以有参数:

[XmlElement ("Customer", Namespace="http://oreilly.com")]
public class CustomerEntity { ... }

参数有2类,positionalnamed,位置参数相当于 Attribute类型的公开构造器的参数,而命名参数相当于 public字段或者public属性。

2.3 Attribute Targets

Attribute有使用的Target,通常是它紧挨着的 Type或者Type成员(字段等)。同时,Attribute也可以运用在程序集上,这需要明确指定这个Attribute的Target

示例: 使用CLSCompliantAtrribute对整个程序集生效:

[assembly:CLSCompliant(true)]

2.4 指定多个Attributes

[Serializable, Obsolete, CLSCompliant(false)]
public class Bar {...}[Serializable] [Obsolete] [CLSCompliant(false)]
public class Bar {...}[Serializable, Obsolete]
[CLSCompliant(false)]
public class Bar {...}

3. Caller Info Attributes

从C#5开始,可以给 可选参数提供下面3种 Caller info Attributes,来让编译器,把调用方的信息传入这个参数中:

  • [CallerMemberName] 调用方 的成员名(member name)
  • [CallerFilePath] 调用方 源代码文件的路径名
  • [CallerLineNumber] 调用方源代码文件中所在的行数

例子:

using System;
using System.Runtime.CompilerServices;
class Program
{static void Main() => Foo();static void Foo ([CallerMemberName] string memberName = null,[CallerFilePath] string filePath = null,[CallerLineNumber] int lineNumber = 0){Console.WriteLine (memberName);    //MainConsole.WriteLine (filePath);     // c:\source\test\Program.csConsole.WriteLine (lineNumber);    //6}
}

上面代码在调用的时候,编译器使用了调用方信息,将它们替换成了参数值, 然后把代码编译成:

static void Main() => Foo ("Main", @"c:\source\test\Program.cs", 6);

Caller Info Attributes对logging很有用,特别当某个属性改变时,需要通知, 实际上.NET里面有个标准接口INotifyPropertyChanged,使用这个很有用:

// 标准接口,里面一个 event委托
public interface INotifyPropertyChanged
{event PropertyChangedEventHandler PropertyChanged;
}// 委托类型
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);// 自定义EventArgs
public class PropertyChangedEventArgs : EventArgs
{public PropertyChangedEventArgs (string propertyName);public virtual string PropertyName { get; }
}

使用:

public class Foo : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged = delegate { };// 主动invoke Delegate的方法void RaisePropertyChanged ([CallerMemberName] string propertyName = null){PropertyChanged (this, new PropertyChangedEventArgs (propertyName));}string customerName;public string CustomerName{get { return customerName; }set{if (value == customerName) return;customerName = value;RaisePropertyChanged();// The compiler converts the above line to:// RaisePropertyChanged ("CustomerName");}}
}

上面的代码,对可选参数propertyName使用了[CallerMemberName], 所以在调用的时候,不需要传递调用放的名字,编译器会自动写入。

4. Dynamic Binding

dynamic binding会将 编译时做的 binding,延迟到运行时。通常是在和动态语言交互的时候使用。

dynamic d = GetSomeObject();
d.Quack();

4.1 Static Binding Versus Dynamic Binding

a. 静态绑定

Duck d = ...
d.Quack();

对于上面的代码,Compiler的搜索顺序:

  1. Duck类中的无参方法Quack
  2. Duck类中的 带有可选参数的 方法Quack
  3. Duck基类的方法
  4. Duck的扩展方法
  5. 以上都没有,报告出编译错误

编译器根据 调用者类型和参数类型来编译,这叫做静态绑定

对于下面这种:

object d = ...
d.Quack();

会有编译错误,因为编译器只知道它的类型(object),而这个类型中找不到方法 Quack

b. 动态绑定

dynamic d = ...
d.Quack();

编译器在编译时不知道它的类型,需要在运行时再进行绑定类型,编译器仅仅把这个表达式打包。

在运行时,如果 动态对象实现了 IDynamicMetaObjectProvider, 那么这个接口就会用来完成绑定; 否则的话就会像compiler知道它的类型一样操作。 这2种分别叫做custom bindinglanguage binding. COM interop可以被视为第三种 dynamic binding

4.2 Custom Binding

custom binding发生在 动态对象实现了IDynamicMetaObjectProvider (IDMOP)接口时。虽然为自己定义的类实现这个接口,但是更多的情况下是我们从 on the DLR .NET实现了的动态语言中获取这么一个对象,比如从 IronPython, IronRuby

自定义使用的例子:

using System;
using System.Dynamic;
public class Test
{static void Main(){dynamic d = new Duck();d.Quack(); // Quack method was calledd.Waddle(); // Waddle method was called}
}public class Duck : DynamicObject
{public override bool TryInvokeMember (InvokeMemberBinder binder, object[] args, out object result){Console.WriteLine (binder.Name + " method was called");result = null;return true;}
}

4.3 Language Binding

Language binding is useful when working around imperfectly designed types or inherent limitations in the .NET type system

static dynamic Mean (dynamic x, dynamic y) => (x + y) / 2;
static void Main()
{int x = 3, y = 4;Console.WriteLine (Mean (x, y));
}

根据设计,language runtime binding尽可能的和静态绑定工作一样,就像在运行时确认了它的类型。也就是说上面的代码,如果把dynamic换成int, 他们行为完全相同。

Dynamic binding越过了静态检查(静态类型安全性),但是它无法绕过动态类型安全性,它不同于反射,可以完全绕过类型安全。

4.4 RuntimeBinderException

动态绑定失败时会抛出RuntimeBinderException

4.5 Runtime Representation of Dynamic

运行时, dynamicobject类型有非常强的相似度,下面代码:

typeof (dynamic) == typeof (object)   // true
typeof (List<dynamic>) == typeof (List<object>)   //true
typeof (dynamic[]) == typeof (object[])       //true

就像object一样,dynamic对象也可以指向任何类型(非pointer):

dynamic x = "hello";
Console.WriteLine (x.GetType().Name); // Stringx = 123; // No error (despite same variable)
Console.WriteLine (x.GetType().Name); // Int32

从结构上来说dynamicobject都是指向对象的引用,可以任意使用它们所指向对象的内容。

通过反射查看dynamic字段,它们被表示成了具有Attributeobject:

public class Test
{public dynamic Foo;
}
//is equivalent to:public class Test
{[System.Runtime.CompilerServices.DynamicAttribute]public object Foo;
}

这样能让一般使用者知道这是 动态类型,同时也能让不支持动态绑定的语言像使用object一样正常工作。

4.5 Dynamic Conversions

运行时类型,就像编译时类型一样,只要类型兼容,就能转换

int i = 7;
dynamic d = i;
long j = d; // No cast required (implicit conversion)int i = 7;
dynamic d = i;
short j = d; // throws RuntimeBinderException

4.6 var vs. dynamic

  • var says, “Let the compiler figure out the type.”
  • dynamic says, “Let the runtime figure out the type.”
dynamic x = "hello"; // 静态类型为dynamic,运行时类型为 string
var y = "hello"; //  静态类型为string,运行时类型为 string
int i = x; //  运行时错误,运行时类型不兼容
int j = y; // 编译时错误,compile-time error (cannot convert string to int)dynamic x = "hello";
var y = x; // Static type of y is dynamic
int z = y; // Runtime error (cannot convert string to int)

4.7 Dynamic Expression

包含动态类型的表达式,整体都是动态类型的,因为他们整体类型信息,都被延迟到了 运行时才知道。使用错误会在运行时抛出异常。

但是关于包含动态类型的表达式的规则,也有一些例外:

1.将动态类型转换成静态类型,会产生静态类型表达式(接收compile-time 检查):

dynamic x = 2;
var y = (int)x; // Static type of y is int

2.调用构造器总是会产生静态类型表达式

dynamic capacity = 10;
var x = new System.Text.StringBuilder (capacity) ;  // 静态类型表达式,dynamic类型发生了转换

3.其他情况,比如传入索引,使用delegate,都会变成 静态类型表达式

4.8 Dynamic Calls Without Dynamic Receivers

通常来说,使用dynamic的时候,总会涉及dynamic reciever,这个相当于调用者:

dynamic x = ...;
x.Foo(); // x is the receiver

但是当没有这个reciever的时候,将dynamic对象作为参数传入 以静态类型作为参数的方法时,具体调用哪个方法同样也是在运行时,根据这个动态对象的类型决定的:

class Program
{static void Foo (int x) { Console.WriteLine ("1"); }static void Foo (string x) { Console.WriteLine ("2"); }static void Main(){dynamic x = 5;dynamic y = "watermelon";Foo (x); // 1Foo (y); // 2Foo (x, x); // Compiler error - wrong number of parametersFook (x); // Compiler error - no such method name}
}

而且当没有dynamic reciever的时候,调用 参数数量不正确,以及不存在的函数,那么编译时就会报错。

4.9 静态类型参与动态表达式绑定

在动态绑定的时候,静态类型信息也会被使用:

static void Foo (object x, object y) { Console.WriteLine ("oo"); }
static void Foo (object x, string y) { Console.WriteLine ("os"); }
static void Foo (string x, object y) { Console.WriteLine ("so"); }
static void Foo (string x, string y) { Console.WriteLine ("ss"); }static void Main()
{object o = "hello";dynamic d = "goodbye";Foo (o, d);               // os
}
// 虽然d的类型是运行时知道的,但是 o的类型之前已经确定了
// 所以动态绑定,会在前2个之间选,最后根据y的类型,选择第二个

4.10 无法动态调用的函数 Uncallable Function

  • 扩展方法
  • 接口的成员,除非 先类型转换成该接口
  • 隐藏的(new)基类成员

理解
动态绑定是根据2部分信息来确定调用的:

  1. 调用的函数名
  2. 调用这个函数对象的 动态类型

然而上面3种无法动态调用的情况,都引入了额外的类型信息

a.扩展方法

扩展方法的使用实际上是编译器的行为, 编译器在编译的时候,根据using指定的命名空间,来搜索扩展方法,编译之后,这个using的概念已经不复存在了。所以运行时无法获取这个包含 扩展方法的类就获取不到了。

b.接口

当调用接口时,也是通过 implicit或者是explicit类型转换,来使用到了这个接口的成员。

interface IFoo { void Test(); }
class Foo : IFoo { void IFoo.Test() {} }IFoo f = new Foo(); // Implicit cast to interface
f.Test();    // 通过接口调用IFoo f = new Foo();
dynamic d = f;
d.Test(); // Exception thrown

上面这个动态类型d,在运行时,丢失了关于IFoo这个类型的信息,所以DLR无法完成动态绑定,它访问不到这个对象(只能获取到 实例对象成员):

Console.WriteLine (f.GetType().Name); // Foo

c. 使用hidden base member

也是通过类型转换或者 base来引入了这一个 additional type, 而这个type的信息,在运行时就丢失了。

Dynamic 部分小结

总的来说,动态绑定就像静态绑定一样,只不过是时间延迟到了运行时。 可以理解为一个对象它有2个类型,一个是静态类型,一个是动态类型。 编译器对静态类型已知的类可以进行编译处理,保证静态类型安全。而对不可知的,则延迟到了运行时执行。 在运行时,可以想象有另一个”编译器”来完成 静态时的操作。

5. 运算符重载(Overloading)

一般用来实现对 自定义结构体的操作符扩展。下面这些操作符可以被重载:

+ (unary) - (unary) ! ˜ ++
-- + - * /
% & | ^ <<
>> == != > <
>= <=

下面这些操作符也可以被重载

  1. implicit和explicit类型转换
  2. truefalse操作符(不是字面值)

下面这些可以被间接的重载:

  1. 复合赋值语句(+=, /= 等等), 通过重载 +,/等等
  2. && and || 通过重载 &和 |

5.1 Operator Functions

定义运算符函数:

  1. 函数名通过 operator关键字跟着一个 操作符符号 来指定
  2. 操作符函数必须是 public static
  3. 操作符函数的参数代表了操作数
  4. 返回值代表了表达式结果
  5. 至少有一个操作数是 这个操作符函数所在的类型
public struct Note
{int value;public Note (int semitonesFromA) { value = semitonesFromA; }// 操作符重载public static Note operator + (Note x, int semitones){return new Note (x.value + semitones);}//public static Note operator + (Note x, int semitones) => new Note (x.value + semitones);
}

5.2 重载 比较操作符

相等和比较操作符经常在定义 struct的时候重载,有时候也会在定义class的时候使用。

定义相等和比较操作符的时候有一些严格的规则

  1. Pairing: C#编译器要求一组操作符需要一起重载: (== 和 !=) , (< 和 >), (<= 和 =>)
  2. Equals and GetHashCode: 大多数情况下,如果重载了==!=,C#编译器会要你也重载GetHashCodeEquals方法
  3. IComparable and IComparable<T>: 如果重载了< ><= =>,那么就应该实现这两个接口

5.3 Custom Implicit and Explicit Conversions

下面是implicit和explicit转换的重载:

public static implicit operator double (Note x) => 440 * Math.Pow (2, (double) x.value / 12 );// Convert from hertz (accurate to the nearest semitone)
public static explicit operator Note (double x) => new Note ((int) (0.5 + 12 * (Math.Log (x/440) / Math.Log(2) ) ));Note n = (Note)554.37; // explicit conversion
double x = n; // implicit conversion

其实对于weakly related types,下面这种方式更好:

  1. 写一个构造器,包含从某个类型转换 的参数
  2. 写一个 ToXXX 方法和 静态的FromXXX方法来完成转换

自定义转换,无法使用 asis

5.4 重载true和false操作符

这种使用情况极少数, 重载false和true操作符,意思是能够使用这个对象本身来进行 逻辑判断:

例子System.Data.SqlTypes.SqlBoolean结构体:

SqlBoolean a = SqlBoolean.Null;
if (a)
Console.WriteLine ("True");
else if (!a)
Console.WriteLine ("False");
else
Console.WriteLine ("Null");

写法:

public struct SqlBoolean
{private byte m_value;public static readonly SqlBoolean Null = new SqlBoolean(0);public static readonly SqlBoolean False = new SqlBoolean(1);public static readonly SqlBoolean True = new SqlBoolean(2);//构造器private SqlBoolean (byte value) { m_value = value; }public static bool operator true (SqlBoolean x)  => x.m_value == True.m_value;public static bool operator false (SqlBoolean x) => x.m_value == False.m_value;public static SqlBoolean operator ! (SqlBoolean x){if (x.m_value == Null.m_value) return Null;if (x.m_value == False.m_value) return True;return False;}}

6. Unsafe Code and Pointers (暂空)

7. Preprocessor Directives (暂空)

Preprocessor directives 告诉了编译器更多的关于 这个区域代码的信息,比如下面:

#define DEBUG
class MyClass
{int x;void Foo(){#if DEBUGConsole.WriteLine ("Testing: x = {0}", x);#endif}...
}

上面这个代码,Foo中的语句是否编译,取决于 DEBUG这个符号是否存在,如果把第一个符号定义移除,那么Foo中的代码就不会被编译。

预处理符号(preprocessor symbols) 可以被定义在一个单独的 源文件中,然后通过制定 /define:symbol命令行参数来传给编译器。

Preprocessor directive Action
#define symbol Defines symbol
#undef symbol Undefines symbol
#if symbol [operator symbol2]... symbol to test
operators are ==, !=, &&, and
#else Executes code to subsequent #endif
#elif symbol [operator symbol2] Combines #else branch and #if test
#endif Ends conditional directives
#warning text text of the warning to appear in compiler output
#error text text of the error to appear in compiler output
#pragma warning [disable | restore] Disables/restores compiler warning(s)
#line [ number ["file"] |hidden] number specifies the line in source code; file is the filename to appear in computer output; hidden instructs debuggers to skip over code from this point until the next #line directive
#region name Marks the beginning of an outline
#endregion Ends an outline region

7.1 Conditional Attributes

标有ConditionalAttribute的部分,仅仅当 特点的 preprocessor symbol存在时才会被编译:

// file1.cs
#define DEBUG
using System;
using System.Diagnostics;
[Conditional("DEBUG")]
public class TestAttribute : Attribute {}
// file2.cs
#define DEBUG
[Test]
class Foo
{
[Test]
string s;
}

7.2 Pragma Warning

编译器有时候会产生一些warning,warning作用是好的,但是有时候产生的warning并不是真的有用。所以你可以选择压制一些自己确定没有问题的warning,来把关注点减小到那些有用的warning上。

下面代码使用#pragma warning directive.让compiler不要警告所有关于Message字段的消息。

public class Foo
{static void Main() { }#pragma warning disable 414static string Message = "Hello";#pragma warning restore 414
}

Omitting the number in the #pragma warning directive disables or restores all warning codes.

8. XML Document

文档注释是用来注释类型或者成员的,以 三个 /开头:

/// <summary>Cancels a running query.</summary>
public void Cancel() { ... }/// <summary>
/// Cancels a running query
/// </summary>
public void Cancel() { ... }/**
<summary> Cancels a running query. </summary>
*/
public void Cancel() { ... }

当编译的时候使用/doc指示,那么编译器就会把这些文档注释拼接在一个XML文件中,有2个作用:

  1. 当这个XML文档在编译后程序集的同一个文件夹时,VS可以读取这些XML文件,来提示IntelliSense
  2. 第三方工具可以把它们变成HTML形式文档

8.1 Standard XML Documentation Tags

  • <summary>

    <summary>...</summary>
    Indicates the tool tip that IntelliSense should display for the type or member; typically a single phrase or sentence.

  • <remarks>

    <remarks>...</remarks>
    Additional text that describes the type or member. Documentation generatorspick this up and merge it into the bulk of a type or member’s description.

  • <param>

    <param name="name">...</param>
    Explains a parameter on a method.

  • <returns>

    <returns>...</returns>
    Explains the return value for a method.

  • <exception>

    <exception [cref="type"]>...</exception>
    Lists an exception that a method may throw (cref refers to the exception type).

  • <permission>

    <permission [cref="type"]>...</permission>
    Indicates an IPermission type required by the documented type or member.

  • <example>

    <example>...</example>
    Denotes an example (used by documentation generators). This usually contains both description text and source code (source code is typically within a<c> or <code>tag).

  • <c>

    <c>...</c>
    Indicates an inline code snippet. This tag is usually used inside an <example>block.

  • <code>

    <code>...</code>
    Indicates a multiline code sample. This tag is usually used inside an <example> block.

  • <see>

    <see cref="member">...</see>
    Inserts an inline cross-reference to another type or member. HTML documentation generators typically convert this to a hyperlink. The compiler emits a warning if the type or member name is invalid. To refer to generic types, use curly braces; for example, cref=”Foo{T,U}”.

  • <seealso>


    Cross-references another type or member. Documentation generators typically write this into a separate “See Also” section at the bottom of the page.

  • <paramref>

    <paramref name="name"/>
    References a parameter from within a or tag.

  • <list>

    <list type=[ bullet | number | table ]>
    <listheader>
    <term>...</term>
    <description>...</description>
    </listheader>
    <item>
    <term>...</term>
    <description>...</description>
    </item>
    </list>
    Instructs documentation generators to emit a bulleted, numbered, or tablestyle list.

  • <para>

    <para>...</para>
    Instructs documentation generators to format the contents into a separate paragraph.

  • <include>

    <include file='filename' path='tagpath[@name="id"]'>...</include>
    Merges an external XML file that contains documentation. The path attribute denotes an XPath query to a specific element in that file.

8.2 Type or Member Cross-References

XML type prefix ID prefixes applied to…
N Namespace
T Type (class, struct, enum, interface, delegate)
F Field
P Property (includes indexers)
M Method (includes special methods)
E Event
! Error
// Namespaces do not have independent signatures
namespace NS
{
/// T:NS.MyClass
class MyClass
{
/// F:NS.MyClass.aField
string aField;
/// P:NS.MyClass.aProperty
short aProperty {get {...} set {...}}
/// T:NS.MyClass.NestedType
class NestedType {...};
/// M:NS.MyClass.X()
void X() {...}
/// M:NS.MyClass.Y(System.Int32,System.Double@,System.Decimal@)
void Y(int p1, ref double p2, out decimal p3) {...}
/// M:NS.MyClass.Z(System.Char[ ],System.Single[0:,0:])
void Z(char[ ] p1, float[,] p2) {...}
/// M:NS.MyClass.op_Addition(NS.MyClass,NS.MyClass)
public static MyClass operator+(MyClass c1, MyClass c2) {...}
/// M:NS.MyClass.op_Implicit(NS.MyClass)˜System.Int32
public static implicit operator int(MyClass c) {...}
/// M:NS.MyClass.#ctor
MyClass() {...}
/// M:NS.MyClass.Finalize
˜MyClass() {...}
/// M:NS.MyClass.#cctor
static MyClass() {...}
}
}

读书笔记: C# 7.0 in a nutshell (第 四 章 Advanced C#- 下)相关推荐

  1. 读书笔记,《刻意练习》,第四章,黄金标准

      有目的的练习虽然很有效,但是他忽略了一些东西.除了简单的集中注意力,并且迫使人们走出舒适区,其实还有一些其他的事情值得我们注意. 比如在前面的记忆训练当中,史蒂夫.法龙能够记住82个数字,而雷尼尽 ...

  2. 精通Web Analytics 2.0 (6) 第四章:点击流分析的奇妙世界:实际的解决方案

    精通Web Analytics 2.0 (6) 第四章:点击流分析的奇妙世界:实际的解决方案 精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第四章:点击流分析的奇妙世界:实际 ...

  3. 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  4. [读书笔记]《算法竞赛入门经典》第1章

    书名:算法竞赛-入门经典 第2版 作者:刘汝佳 类别:读书笔记 文章目录 前言 第1章 程序设计入门 1.1 算术表达式 1.2 变量及其输入 1.3 顺序结构程序设计(Sequential Prog ...

  5. 读书笔记 计算机系统--系统架构与操作系统的高度集成 第一章概叙

    大二下开始意识到计算机基础的重要性,就去图书馆随缘找了一本机械工业出版社的大部头书籍开始看, 也为了让自己养成总结和记录的习惯,把每一章的内容结构化的写成读书笔记.以后查阅方便. 这本书是集成的,计算 ...

  6. Box2D v2.3.0 用户指南(第四章)

     第四章 碰撞模块(Collision Module) 4.1简介 碰撞模块包含形状(shape)以及操作它们的函数.此外,碰撞模块还包括dynamictree和broad-phase来加快大型系 ...

  7. 读书笔记《从0到1--开启商业与未来的秘密》

    从0到1--开启商业与未来的秘密 Zero to One--Notes on Startups,Or How to Build The Future [美]Peter Thiel,Blake Mast ...

  8. 读书笔记之《C语言深度剖析》第一章:关键字

    第一章引言 什么是定义及声明? 定义:定义是编译器创建一个对象,并且为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名. 声明:1.告诉编译器该变量名已经匹配了一个内存 ...

  9. 读书笔记《数据挖掘概念与技术》第2章 数据预处理 2.4 数据集成和变换

    <数据挖掘:概念与技术(原书第2版)> 2.4 数据集成和变换 数据挖掘经常需要数据集成-合并来自多个数据存储的数据.数据还可能需要转换成适于挖掘的形式.本节介绍数据集成和数据变换. 2. ...

  10. python基础学习[python编程从入门到实践读书笔记(连载六)]:数据可视化项目第17章

    文章目录 使用API end 项目结果: 使用plotly可视化github最受欢迎的python仓库: 修改后的可视化图表: 使用API 编写独立的程序,实现对获取的数据可视化.我们使用Web AP ...

最新文章

  1. 优秀程序员应该做的几件事【转】
  2. 首个镜子分割网络问世,大连理工、鹏城实验室、香港城大出品 | ICCV 2019
  3. Android的事件分发
  4. P2611-[ZJOI2012]小蓝的好友【Treap,扫描线】
  5. Spring Data Solr教程:向所有存储库添加自定义方法
  6. flutter 如何判断在哪个页面_Agora 教程:构建你的第一个 Flutter 视频通话应用
  7. oracle怎么判断地址相似,如何查看oracle数据文件的地址
  8. SAP License:SAP精细化的应收付及要素为根设计思想
  9. Bailian3256 矩阵的乘法【数学计算】
  10. linux移植简介[MS2]
  11. dnn神经网络_Facebook AI新研究:可解释神经元或许会阻碍DNN的学习
  12. c语言程序设计实验第二版答案,C语言程序设计实验指导及习题答案
  13. pyshp读写shapefile
  14. 基于JAVA社团管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
  15. 一个性价比超高的英语口语平台
  16. pandas 数据读取与保存
  17. 诚之和:各业务不断爆出裁员,字节跳动如何“过冬”?
  18. 【车载】度(角度)和弧度的概念
  19. 7. 伪随机数的生成
  20. android手机或者平板重力感应器描述

热门文章

  1. 特殊字符的处理 GS RS EOT
  2. 关于Python、R、VBA、SAS的生成批量变量名与动态变量引用的问题
  3. linux 内核出现 oops 如何调试
  4. OpenChannelSSD之六_从OpenChannelSSD到ZNS
  5. 乐1s 乐视X501_官方线刷包_救砖包_解账户锁
  6. netbeans莫明其妙的报错
  7. chrome更新到80以上版本后,带来的跨域请求cookie丢失问题
  8. 【DZX修改】根据性别不同显示不同的默认头像
  9. python实现有趣的数学逻辑程序
  10. CC++数组练习题(头歌)朋友圈点赞