CLR via C# 读书笔记---常量、字段、方法和参数
常量
常量
是值从不变化的符号。定义常量符号时,它的值必须能在编译时确定。确定后,编译器将唱两只保存在程序集元数据中。使用const
关键字声明常量。由于常量值从不变化,所以常量总是被视为类型定义的一部分。换言之,常量总是被视为静态成员,而不是实例成员。常量的值直接潜入代码,在运行时不需要为常量分配任何内存。
字段
字段是一种数据成员,其中容纳了一个值类型的实例或者一个对引用类型的引用。由于字段存储在动态类型中,所以它们的值在运行时才能获取。字段还解决了常量存在的版本控制问题。字段可以是任何数据类型。
CLR支持readonly
字段和 read/write
字段,大多数字段是read/write
字段,意味着在代码执行过程中,字段值可多次改变,但readonly
字段只能在构造器方法中写入。
提示:
当某个字段是引用类型,并且该字段被标记为readonly
时,不可改变的是引用,而非字段引用的对象。
public sealed class AType {
// InvalidChars must always refer to the same array object
public static readonly Char[] InvalidChars = new Char[] { 'A', 'B', 'C' };
}
public sealed class AnotherType {
public static void M() {
// The lines below are legal, compile, and successfully
// change the characters in the InvalidChars array
//编译成功没什么问题
AType.InvalidChars[0] = 'X';
AType.InvalidChars[1] = 'Y';
AType.InvalidChars[2] = 'Z';
// The line below is illegal and will not compile because
// what InvalidChars refers to cannot be changed
// 非法的,无法通过编译
//AType.InvalidChars = new Char[] { 'X', 'Y', 'Z' };
}
}
方法
实例构造器和类
构造器
是将类型的实例初始化为良好状态的特殊方法。构造器在方法定义源数据表
中始终叫做.ctor
(vs构造函数的代码段就是ctor
),创建引用类型的实例时,首先
为实例的数据字段分配内存,然后
初始化对象的附加字段,最后
调用类型的实例构造器来设置对象的初始状态。
构造引用类型的对象是,在调用实力构造器之前,为对象分配的内存总总是先被归零,没有被构造器显式重写的所有字段都保证获得0
或null
实例构造器永远不能被继承,一个类型可以定义多个实力构造器,每个构造器都必须有不同的签名。
C#用简单的语法在构造引用类型的实例时初始化类型定义中的字段
internal sealed class SomeType {
private Int32 m_x = 5;
}
SomeType
的构造器先把5
存到m_x,在调用基类的构造器。
如果有几个以初始化的实力字段和许多重载的构造器方法,可以创建单个构造器来执行这些公共的初始化,然后让其他构造器都显式调用这个公共的构造器,这样能减少代码。
internal sealed class SomeType {
// Do not explicitly initialize the fields here
private Int32 m_x;
private String m_s;
private Double m_d;
private Byte m_b;
// This method MUST be called by all constructors.
private void SetFieldDefaults() {
m_x = 5;
m_s = "Hi there";
m_d = 3.14159;
m_b = 0xff;
}
// This constructor sets all fields to their default.
public SomeType() {
SetFieldDefaults();
}
// This constructor sets all fields to their default, then changes m_x.
public SomeType(Int32 x) {
SetFieldDefaults();
m_x = x;
}
// This constructor sets all fields to their default, then changes m_s.
public SomeType(String s) {
SetFieldDefaults();
m_s = s;
}
// This constructor sets all fields to their default, then changes m_x & m_s.
public SomeType(Int32 x, String s) {
SetFieldDefaults();
m_x = x;
m_s = s;
}
}
实例构造器和结构
CLR总是允许创建值类型的实例,并且没有办法阻止值类型的实例化。值类型其实并不需要定义构造器,但CLR确实允许为值类型定义构造器。但必须显式调用
才能执行
。结构不能包含显式的无参构造器。没有无参构造器,值类型的字段总是被初始化为0
或null
扩展方法
要想定义自己的扩展方法
,只需要在第一个参数前面添加this
关键字。
规则和原则
- C#只支持扩展方法,不支持扩展属性、扩展事件、扩展操作符。
- 扩展方法必须在非泛型的静态类中声明,类型没有限制,只有第一个参数前面能用
this
关键字标记 - 编译器在查找扩展方法时,要求静态类文件本身必须必有文件作用域,扩展方法必须在顶级静态类中作用,不鞥你在嵌套类中定义
- 多个静态类可以定义相同的扩展方法,编译器检测到多个扩展方法,会提示
方法调用不明确
- 扩展方法存在版本控制问题,将来
Microsoft
添加和你一样的扩展方法,程序就会有不同的行为。
来一个扩展方法栗子:
internal static class StringBuilderExtensions {
public static Int32 IndexOf(this StringBuilder sb, Char value) {
for (Int32 index = 0; index < sb.Length; index++)
if (sb[index] == value) return index;
return -1;
}
}
参数
可选参数和命名参数
private static Int32 s_n = 0;
public static void Go() {
ImplicitlyTypedLocalVariables();
// 1. Same as: M(9, "A", default(DateTime), new Guid());
// 直接调用M
M();
// 2. Same as: M(8, "X", default(DateTime), new Guid());
// 前两个参数指定,后两个是默认值
M(8, "X");
// 3. Same as: M(5, "A", DateTime.Now, Guid.NewGuid());
// 使用明明参数指定值
M(5, guid: Guid.NewGuid(), dt: DateTime.Now);
// 4. Same as: M(0, "1", default(DateTime), new Guid());
// s_n先传入0 ,然后加1,把1传入,再加1 ,此时s_n是 2
M(s_n++, s_n++.ToString());
// 5. Same as: String t1 = "2"; Int32 t2 = 3;
// M(t2, t1, default(DateTime), new Guid());
// 2传入 s。然后加1,传入x
M(s: (s_n++).ToString(), x: s_n++);
}
private static void M(Int32 x = 9, String s = "A",
DateTime dt = default(DateTime), Guid guid = new Guid()) {
Console.WriteLine("x={0}, s={1}, dt={2}, guid={3}", x, s, dt, guid);
}
规则和原则
- 可为方法、构造器方法和有参属性的参数指定默认值。
- 有默认值的参数必须放在没有默认值的所有参数之后。换言之,一旦定义了有默认值的参数,它右边的所有参数也必须有默认值。
- 默认值必须是编译时能确定的常量值
- 不要重命名参数变量,否则任何顶用着已传参数名的方式传递实参代码也必须得修改
- 如果方法从模块外部调用,更改参数的默认的默认值具有潜在的危险性
- 如果参数用
ref
或out
关键字进行了标识,就不能设置默认值
隐式类型的局部变量
var
关键字的真正价值就是让程序员少打几个字。
以传引用的方式向方法传递参数
即C#中ref
和out
关键字。以前用的不做,现在看来得经常使用。
先看一下out
的栗子
public static void Main()
{
Int32 X; //没有初始化
GetValue(out x); //
Console.WriteLine(x); //显式10
}
private static void GetValue(out Int32 v)
{
v = 10; // 在这里面必须初始化
}
在看一个ref
的栗子
public static void Main() {
Int32 x = 5; // x is 初始化的字段
AddVal(ref x); // x must be initialized.
Console.WriteLine(x); // Displays "15"
}
private static void AddVal(ref Int32 v) {
v += 10; // This method can use the initialized value in v.这方法可以改变已初始化的值
}
为值类型使用out
和ref
,等同于以传值的方式传递引用类型。
暂时就介绍上面两种使用ref
和out
的方式。
向方法传递可变数量的参数
通过使用param
关键字完成
private static Int32 Add(params Int32[] values) {
// NOTE: it is possible to pass the 'values'
// array to other methods if you want to.
Int32 sum = 0;
for (Int32 x = 0; x < values.Length; x++)
sum += values[x];
return sum;
}
Console.WriteLine(Add(1, 2, 3, 4, 5));//显式15
参数和返回类型的设计规范
声明方法的参数类型时,应尽量指定最弱的类型,宁愿要结构也不要基类。而返回类型要声明为最强的类型
总结
今天的依然是非常无聊但却非常基础的文章,比如扩展方法
、ref
、out
、默认参数
等等这些都在实际编程中是经常用到的,必须要掌握牢固,今天一个微软亚洲研究院大数据系列讲座
开课了,努力坚持学完。
CLR via C# 读书笔记---常量、字段、方法和参数的更多相关文章
- CLR via c#读书笔记五:方法
注:书本第8章:方法 实例构造器和类(引用类型) 构造器方法在“方法定义元数据表”中始终叫做.ctor(constructor的简称). 构造引用类型的对象,在调用类型的实例构造器之前,为对象分配的内 ...
- 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)
强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...
- clr via c#读书笔记五:常量和字段
1.常量是值从不变化的符号.只能定义编译器识别的基元类型的常量.如:Boolean,Char,Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,Single,Dou ...
- 《C#图解教程》读书笔记之三:方法
本篇已收录至<C#图解教程>读书笔记目录贴,点击访问该目录可获取更多内容. 一.方法那些事儿 (1)方法的结构:方法头—指定方法的特征,方法体—可执行代码的语句序列: (2)方法的调用:参 ...
- CLR via c#读书笔记八:泛型
1.定义泛型类型或方法时,为类型指定的任何变量(比如T)都称为类型参数.使用泛型类型或方法时指定的具体数据类型称为类型实参. 2.System.Collections.Concurrent命名空间提供 ...
- [Clr via C#读书笔记]Cp15枚举和位标识
Cp15枚举和位标识 枚举类型 本质是结构,符号名称-值:好处显而易见:System.Enum;值类型: 编译的时候,符号会转换为常量字段: 枚举支持很多方法和成员: 位标识bit flag 判断和设 ...
- CLR via c#读书笔记六:参数
注:书本第9单参数 CLR默认所有方法参数都传值.引用本身是值引的,意味左方法能修改对象,而调用都能看到这些修改.值类型,传的是实例的一个副本,所以调用者不受影响. (和以前理解的不一样.默认都是传值 ...
- [Clr via C#读书笔记]Cp17委托
Cp17委托 简单介绍 delegate回调函数机制,可以理解存储函数地址的变量类型: 类型安全: 引用类型支持逆变和协变: 回调 静态方法,实例方法 委托的本质 所有的委托都派生自System.Mu ...
- [Clr via C#读书笔记]Cp7常量和字段
Cp7常量和字段 常量 常量在编译的时候必须确定,只能一编译器认定的基元类型.被视为静态,不需要static:直接嵌入IL中: 区别ReadOnly 只能在构造的时候初始化,内联初始化. 字段 数据成 ...
随机推荐
- Android AlertDialog去除黑边白边自定义布局(转)
LayoutInflater inflater = this.getLayoutInflater(); View view = inflater.inflate(R.layout.test_alert ...
- Premiere Pro & After Effects插件开发调试方法
在给Adobe Premiere Pro(PR)和Adobe After Effects(AE)插件开发时,对于实时调试插件有着很强的需求.除了业务需求外,单步调试插件还能够摸清楚Plugin和Hos ...
- C++如何调用C#开发的dll
序言 本文介绍一个C++如何调用C#开发的dll实例. 前言 C++编写的程序为非托管代码,C#编写的程序为托管代码.托管代码虽然提供了其他开发平台没有的许多优势,但由于前期系统及历史版本很多使用的是 ...
- 【MVVM Light】新手初识MVVM,你一看就会
一.前言 作为一个初入软件业的新手,各种设计模式与框架对我是眼花缭乱的.所以当我接触到这些新知识的时候就希望自己能总结几个步骤,以便更好更方便的在日常工作中进行使用. MVVM顾名思义就是Model- ...
- [.NET逆向] .net IL 指令速查(net破解必备)
.net的破解比较特殊,很多人看见IL就头疼,最近在研究的时候发现了这个东东 相信对广大学习net破解的人一定有帮助 .对上指令表一查,跟读原代码没什么区别了, 名称 说明 Add 将两个值相加并将结 ...
- 【Win10开发】响应式布局——AdaptiveTrigger
接触过Windows10的童鞋已经知道Universal app不仅可以运行在pc上,还可以运行在mobile或者其他平台上.那么这样势必会带来一个问题,如何针对不同屏幕来进行布局适配.所以微软提供了 ...
- Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室)
简介 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端 ...
- C++11之std::function和std::bind
std::function是可调用对象的包装器,它最重要的功能是实现延时调用: #include "stdafx.h" #include<iostream>// std ...
- Scalaz(56)- scalaz-stream: fs2-安全运算,fs2 resource safety
fs2在处理异常及资源使用安全方面也有比较大的改善.fs2 Stream可以有几种方式自行引发异常:直接以函数式方式用fail来引发异常.在纯代码里隐式引发异常或者在运算中引发异常,举例如下: /函数 ...
- Tomcat源代码-门面模式(Facade)
从Tomcat源码提炼出设计模式-门面设计模式: 概念 外部访问内部,耦合度增加,不利于扩展.而门面模式在内部基础上进行再度封装,只提供外部想要的方法.这时访问方式由“外部---内部”变为了“外部-- ...