C#笔记(三)-- Program building blocks, Major language areas
Member
The following list provides an overview of the kinds of members a class can contain.
- Constants: Constant values associated with the class
- Fields: Variables that are associated of the class
public class Color
{
// static field, read-only fields
// 只能在字段声明期间或在同一个类的构造函数中向只读字段赋值。
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
// instance field
public byte R;
public byte G;
public byte B;
public Color(byte r, byte g, byte b)
{
R = r;
G = g;
B = b;
}
}
- Methods: Actions that can be performed by the class
//在声明方法的类中,方法的 签名 必须是唯一的。 方法签名包含方法名称、类型参数数量及其参数的数量、修饰符和类型。 方法签名不包含返回类型。
//When a method body is a single expression, the method can be defined using a compact expression format
public override string ToString() => "This is an object";
//Parameters: Parameters are used to pass values or variable references to methods
// 有四类参数:值参数(修改值形参不会影响为其传递的实参)、引用参数(ref)、输出参数(out)和参数数组。
// ref 引用参数
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
public static void SwapExample()
{
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine($"{i} {j}"); // "2 1"
}
// output parameter
static void Divide(int x, int y, out int result, out int remainder)
{
result = x / y;
remainder = x % y;
}
public static void OutUsage()
{
Divide(10, 3, out int res, out int rem);
Console.WriteLine($"{res} {rem}"); // "3 1"
}
// 参数数组 允许向方法传递数量不定的自变量。 参数数组使用 params 修饰符进行声明。 参数数组只能是方法的最后一个参数,且参数数组的类型必须是一维数组类型。
public class Console
{
public static void Write(string fmt, params object[] args) { }
public static void WriteLine(string fmt, params object[] args) { }
// ...
}
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
- Properties: Actions associated with reading and writing named properties of the class
- Indexers: Actions associated with indexing instances of the class like an array
- Events: Notifications that can be generated by the class
- Operators: Conversions and expression operators supported by the class
- Constructors: Actions required to initialize instances of the class or the class itself
- Finalizers: Actions performed before instances of the class are permanently discarded
- Types: Nested types declared by the class
Accessibility
- public: Access isn't limited.
- private: Access is limited to this class.
- protected: Access is limited to this class or classes derived from this class.
- internal: Access is limited to the current assembly (.exe or .dll).
- protected internal: Access is limited to this class, classes derived from this class, or classes within the same assembly.
- private protected: Access is limited to this class or classes derived from this type within the same assembly.
虚方法、重写方法和抽象方法
虚方法
实例方法声明中有 virtual 修饰符
When a virtual method is invoked, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke.
In a nonvirtual method invocation, the compile-time type of the instance is the determining factor.
可以在派生类中 重写 虚方法。 如果实例方法声明中有 override 修饰符,那么实例方法可以重写签名相同的继承虚方法
抽象方法
抽象方法 是没有实现代码的虚方法。 抽象方法使用 abstract 修饰符进行声明,仅可在抽象类中使用。 必须在所有非抽象派生类中重写抽象方法
属性,事件(Property, event)
属性
属性是字段的自然扩展。 两者都是包含关联类型的已命名成员,用于访问字段和属性的语法也是一样的。 不过,与字段不同的是,属性不指明存储位置。 相反,属性包含访问器,用于指定在读取或写入属性值时执行的语句。
属性的声明方式与字段相似,区别是属性声明以在分隔符 { 和 } 之间写入的 get 访问器或 set 访问器结束,而不是以分号结束。 同时具有 get 访问器和 set 访问器的属性是“读写属性”。 只有 get 访问器的属性是“只读属性”。 只有 set 访问器的属性是“只写属性”。
事件
借助 事件 成员,类或对象可以提供通知。 事件的声明方式与字段类似,区别是事件声明包括 event 关键字,且类型必须是委托类型。
在声明事件成员的类中,事件的行为与委托类型的字段完全相同(前提是事件不是抽象的,且不声明访问器)。 字段存储对委托的引用,委托表示已添加到事件的事件处理程序。 如果没有任何事件处理程序,则字段为 null。
例子
public class MyList<T>
{
const int DefaultCapacity = 4;
T[] _items;
int _count;
public MyList(int capacity = DefaultCapacity)
{
_items = new T[capacity];
}
public int Count => _count;
//属性
public int Capacity
{
get => _items.Length;
set
{
if (value < _count) value = _count;
if (value != _items.Length)
{
T[] newItems = new T[value];
Array.Copy(_items, 0, newItems, 0, _count);
_items = newItems;
}
}
}
public T this[int index]
{
get => _items[index];
set
{
_items[index] = value;
OnChanged();
}
}
public void Add(T item)
{
if (_count == Capacity) Capacity = _count * 2;
_items[_count] = item;
_count++;
OnChanged();
}
// Changed 事件由 OnChanged 虚方法引发,此方法会先检查事件是否是 null(即不含任何处理程序)。
// 引发事件的概念恰恰等同于调用由事件表示的委托。
protected virtual void OnChanged() =>
Changed?.Invoke(this, EventArgs.Empty);
public override bool Equals(object other) =>
Equals(this, other as MyList<T>);
static bool Equals(MyList<T> a, MyList<T> b)
{
if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
if (Object.ReferenceEquals(b, null) || a._count != b._count)
return false;
for (int i = 0; i < a._count; i++)
{
if (!object.Equals(a._items[i], b._items[i]))
{
return false;
}
}
return true;
}
//声明一个 Changed 事件成员,指明已向列表添加了新项。
public event EventHandler Changed;
public static bool operator ==(MyList<T> a, MyList<T> b) =>
Equals(a, b);
public static bool operator !=(MyList<T> a, MyList<T> b) =>
!Equals(a, b);
}
// 客户端通过 事件处理程序 响应事件。 使用 += 和 -= 运算符分别可以附加和删除事件处理程序。 下面的示例展示了如何向 MyList<string> 的 Changed 事件附加事件处理程序。
// 对于需要控制事件的基础存储的高级方案,事件声明可以显式提供 add 和 remove 访问器,这与属性的 set 访问器类似。
class EventExample
{
static int s_changeCount;
static void ListChanged(object sender, EventArgs e)
{
s_changeCount++;
}
public static void Usage()
{
var names = new MyList<string>();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(s_changeCount); // "3"
}
}