类型相似,但与它无关)。 public abstract class Expression public abstract double Evaluate(Dictionary<string, object> vars); public class Constant Expression double value public Constant(double value value value public override double Evaluate(Dictionary<string, object> vars) return value public class VariableReference Expression string name; public VariableReference(string name) public override double Evaluate (Dictionary<string, object> vars) object value vars[name]?? throw new Exception($"Unknown variable: name)"); return Convert ToDouble(value) public class Operation Expression on Expression right public Operation(Expression left, char op, Expression right) left left: right right; public override double Evaluate(Dictionary<string, object> vars) double x=left Evaluate (vars) double y =right. Evaluate(vars) tch case return return x yi y; default: throw new Exception("Unknown operator ");
public abstract class Expression { public abstract double Evaluate(Dictionary<string, object> vars); } public class Constant : Expression { double _value; public Constant(double value) { _value = value; } public override double Evaluate(Dictionary<string, object> vars) { return _value; } } public class VariableReference : Expression { string _name; public VariableReference(string name) { _name = name; } public override double Evaluate(Dictionary<string, object> vars) { object value = vars[_name] ?? throw new Exception($"Unknown variable: {_name}"); return Convert.ToDouble(value); } } public class Operation : Expression { Expression _left; char _op; Expression _right; public Operation(Expression left, char op, Expression right) { _left = left; _op = op; _right = right; } public override double Evaluate(Dictionary<string, object> vars) { double x = _left.Evaluate(vars); double y = _right.Evaluate(vars); switch (_op) { case '+': return x + y; case '-': return x - y; case '*': return x * y; case '/': return x / y; default: throw new Exception("Unknown operator"); } } } 类型相似,但与它无关)
上面的四个类可用于进行算术表达式建模。例如,使用这些类的实例,可以按如下方式表示表达式x+3。 Expression e= new Operation( new VariableReference("x") new Constant(3)); 调用 Expression实例的 Evaluate方法可以计算给定的表达式并生成 double值。此方法需要使用自变量 Dictionary,其中包含变量名称(作为项键)和值(作为项值)。因为 Evaluate是一个抽象方法因此派生自 expression的非抽象类必须替代 Evaluate Constant的 Evaluate实现代码只返回存储的常量。 VariableReference实现代码查找宇典中的变量名称,并返 回结果值。 Operation实现代码先计算左右操作数(以递归方式调用其 Evaluate方法)然后执行给定的算术运 以下程序使用 Expression类根据不同的x和y值计算表达式x:(y+2) Expression e= new Operation( new VariableReference ("x"), new Operation( new VariableReference("y"), new Constant(2) Dictionary<string, object> vars new Dictionary<string, object>o) vars["x"]=3 vars["y"]=5 Console. WriteLine(e. Evaluate(vars));//"21 vars["x"]=1.5 vars["y"]=9; Console. WriteLine(e. Evaluate(vars));//16.5 方法重载 借助方法重载,同一类中可以有多个同名的方法,只要这些方法具有唯一签名即可。编译如何调用重载的方法 时,编译器使用重载决策来确定要调用的特定方法。重载决策会査找与自变量匹配度最高的一种方法。如果找 不到任何最佳匹配项,则会报告错误。下面的示例展示了重载决策的实际工作方式。 UsageExample方法中每个 调用的注释指明了调用的方法
Expression e = new Operation( new VariableReference("x"), '+', new Constant(3)); Expression e = new Operation( new VariableReference("x"), '*', new Operation( new VariableReference("y"), '+', new Constant(2) ) ); Dictionary<string, object> vars = new Dictionary<string, object>(); vars["x"] = 3; vars["y"] = 5; Console.WriteLine(e.Evaluate(vars)); // "21" vars["x"] = 1.5; vars["y"] = 9; Console.WriteLine(e.Evaluate(vars)); // "16.5" 方法重载 上面的四个类可用于进行算术表达式建模。 例如,使用这些类的实例,可以按如下方式表示表达式 x + 3 。 调用 Expression 实例的 Evaluate 方法可以计算给定的表达式并生成 double 值。 此方法需要使用自变量 Dictionary ,其中包含变量名称(作为项键)和值(作为项值)。 因为 Evaluate 是一个抽象方法,因此派生自 Expression 的非抽象类必须替代 Evaluate 。 Constant 的 Evaluate 实现代码只返回存储的常量。 VariableReference 实现代码查找字典中的变量名称,并返 回结果值。 Operation 实现代码先计算左右操作数(以递归方式调用其 Evaluate 方法),然后执行给定的算术运 算。 以下程序使用 Expression 类根据不同的 x 和 y 值计算表达式 x * (y + 2) 。 借助方法 重载,同一类中可以有多个同名的方法,只要这些方法具有唯一签名即可。 编译如何调用重载的方法 时,编译器使用 重载决策 来确定要调用的特定方法。 重载决策会查找与自变量匹配度最高的一种方法。 如果找 不到任何最佳匹配项,则会报告错误。 下面的示例展示了重载决策的实际工作方式。 UsageExample 方法中每个 调用的注释指明了调用的方法
lass Over Example static void FO=> Console. WriteLine(FO"); static void F(object x)=>Console. WriteLine ("F(object)") static void F(int x)=> Console. WriteLine ("F(int)"); static void F(double x)=>Console. WriteLine("F(double)"); static void F<T>(T x)=>Console. WriteLine ("F<T>(T)") static void F(double x, double y)=>Console. WriteLine("F(double, double)") public static void Usage Example( F(); / Invokes FO) F(1) / Invokes F(int) F(1.8) / Invokes F(double) FC"abc"); / Invokes F<string>(string) F((double )1); / Invokes F(double) ((object)1); / Invokes F(object) F<int>(1) / Invokes F<int>(int) F(1,1); / Invokes F(double, double) 如示例所示,可将自变量显式转换成确切的参数类型和类型自变量,随时选择特定的方法。 其他函数成员 包含可执行代码的成员统称为类的函数成员。上一部分介绍了作为主要函数成员类型的方法。此部分捋介绍 C#支持的其他类型函数成员:构造函数、属性、索引器、事件、运算符和终结器 下面的示例展示了 MyList泛型类,用于实现对象的可扩充列表。此类包含最常见类型函数成员的多个示 public class MyList<T> const int Defaultcapacity =4 public MyList(int capacity= DefaultCapacity) items = new T[capacity] public int Count =>count public int Capacity et => items. Lengt if (value< count) value=count if (value I=items. Length) T[] newItems new T[value] Array Copy( items, 0, newItems, 0, count) items newItems; T this[int index
class OverloadingExample { static void F() => Console.WriteLine("F()"); static void F(object x) => Console.WriteLine("F(object)"); static void F(int x) => Console.WriteLine("F(int)"); static void F(double x) => Console.WriteLine("F(double)"); static void F<T>(T x) => Console.WriteLine("F<T>(T)"); static void F(double x, double y) => Console.WriteLine("F(double, double)"); public static void UsageExample() { F(); // Invokes F() F(1); // Invokes F(int) F(1.0); // Invokes F(double) F("abc"); // Invokes F<string>(string) F((double)1); // Invokes F(double) F((object)1); // Invokes F(object) F<int>(1); // Invokes F<int>(int) F(1, 1); // Invokes F(double, double) } } 其他函数成员 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 如示例所示,可将自变量显式转换成确切的参数类型和类型自变量,随时选择特定的方法。 包含可执行代码的成员统称为类的 函数成员。 上一部分介绍了作为主要函数成员类型的方法。 此部分将介绍 C# 支持的其他类型函数成员:构造函数、属性、索引器、事件、运算符和终结器。 下面的示例展示了 MyList<T> 泛型类,用于实现对象的可扩充列表。 此类包含最常见类型函数成员的多个示 例
items[index]= value UnChanged(; f (count = Capacity Capacity =count *23 On Changed public override bool Equals(object other)=> static bool Equals(MyList<T> a, MyList<T> b) if (object Reference Equals(a, null)) return Object. ReferenceEquals(b, null) f(Object. ReferenceEquals(b, null)I a. count I= 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 public event EventHandler Changed; public static bool operator ==(MyList<T> a, MyList<T> b)=> Equals(a, b); public static bool operator I=(MyList<T> a, MyList<T> b)=> 构造函数 C#支持实例和静态构造函数。实例构造郾数是实现初始化类实例所需执行的操作的成员。静态构造函数是实 现在首次加载类时初始化类本身所需执行的操作的成员。 构造函数的声明方式与方法一样都没有返回类型且与所含类同名。如果构造函数声明包含stc修饰符 则声明的是静态构造函数。否则,声明的是实例构造图数 实例构造函数可重载并且可具有可选参数。例如, MyList>类声明一个具有单个可选int参数的实例构造函 数。实例构造函数使用new运算符进行调用。下面的语句使用包含和不包含可选自变量的List类构造函数 来分配两个 MyList<string>实例 MyList<string> list1= new MyList<string>O MyList<string> list2 new MyList<string>(10); 与其他成员不同,实例构造函数不会被继承。类中只能包含实际上已在该类中声明的实例构造函数。如果没有 为类提供实例构造函数,则会自动提供不含参数的空实例构造函数。 属性 属性是字段的自然扩展。两者都是包含关联类型的已命名成员,用于访问字段和属性的语法也是一样的。不
set { _items[index] = value; OnChanged(); } } public void Add(T item) { if (_count == Capacity) Capacity = _count * 2; _items[_count] = item; _count++; OnChanged(); } 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; } 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> list1 = new MyList<string>(); MyList<string> list2 = new MyList<string>(10); “属性” C# 支持实例和静态构造函数。 实例构造函数 是实现初始化类实例所需执行的操作的成员。 静态构造函数是实 现在首次加载类时初始化类本身所需执行的操作的成员。 构造函数的声明方式与方法一样,都没有返回类型,且与所含类同名。 如果构造函数声明包含 static 修饰符, 则声明的是静态构造函数。 否则,声明的是实例构造函数。 实例构造函数可重载并且可具有可选参数。 例如, MyList<T> 类声明一个具有单个可选 int 参数的实例构造函 数。 实例构造函数使用 new 运算符进行调用。 下面的语句使用包含和不包含可选自变量的 MyList 类构造函数 来分配两个 MyList<string> 实例。 与其他成员不同,实例构造函数不会被继承。 类中只能包含实际上已在该类中声明的实例构造函数。 如果没有 为类提供实例构造函数,则会自动提供不含参数的空实例构造函数。 属性 是字段的自然扩展。 两者都是包含关联类型的已命名成员,用于访问字段和属性的语法也是一样的。 不
过,与字段不同的是,属性不指明存储位置。相反,属性包含访问器,用于指定在读取或写入属性值时执行的语 属性的声明方式与字段相似区别是属性声明以在分隔符(和}之间写的get访间器或set访问器结束而 不是以分号结束。同时具有get访问器和set访问器的属性是读写属性"。只有get访问器的属性是“只读属 性"。只有set访问器的属性是“只写属性"。 get访问器对应于包含属性类型的返回值的无参数方法。set访问器对应于包含一个名为vaue的参数但不含返 回类型的方法。get访问器会计算属性的值。set访问器会为属性提供新值。当属性是赋值的目标或者是艹 的操作数时,会调用set访问器。在引用了属性的其他情况下,会调用get访问器。 tT类声明以下两个属性:cumt和 Capacity(分别为只读和读写)。以下示例代码展示了如何使用这些 属性 MyList<string> names new MyList<string>O; names. Capacity =100 Invokes set accessor int i= names. Count; / Invokes get accessor int j= names. Capacity; / Invokes get accessor 类似于字段和方法,C#支持实例属性和静态属性。静态属性使用静态修饰符进行声明,而实例属性则不使用静 态修饰符进行声明 属性的访间器可以是虚的。如果属性声明包含 virtual、 abstract或 override修饰符,则适用于属性的访间 索引器 借助索引器成员,可以捋对象编入索引(像处理数组一样)。索引器的声明方式与属性类似,不同之处在于,索 引器成员名称格式为this后跟在分隔符[和]内写入的参数列表。这些参数在索引器的访问器中可用。类 似于属性,索引器分为读写、只读和只写索引器,且索引器的访问器可以是虚的。 刚 yListT、类声明一个需要使用int参数的读写索引器。借助索引器可以使用nt值将st、实例编 入索引。例如 MyList<string> names new MyList<string>O; names. Add("Liz"); names. Add("Martha") names. Add("Beth"); for (int i =0; i names Count; i++) string 5= names[il names[i]= 5. ToUpper() 索引器可被重载。一个类可声明多个索引器.只要其参数的数量或类型不同即可。 事1 借助李件成员,类或对象可以提供通知。事件的声明方式与字段类似,区别是事件声明包括eet关键字,且 类型必须是委托类型 在声明事件成员的类中,事件的行为与委托类型的字段完全相同(前提是事件不是抽象的,且不声明访问器)。字 段存储对委托的引用,委托表示已添加到事件的事件处理程序。如果没有任何事件处理程序,则字段为mu1l yits类声明一个 Changed事件成员,指明已向列表添加了新项。 Changed事件由 Unchanged虛方法引 发,此方法会先检查事件是否是mu11(即不含任何处理程序)。引发事件的概念恰恰等同于调用由事件表示的委 托。不存在用于引发事件的特殊语营构造 客户端通过事件处理程序响应事件。使用+=和-运算符分别可以附加和删除事件处理程序。下面的示例
MyList<string> names = new MyList<string>(); names.Capacity = 100; // Invokes set accessor int i = names.Count; // Invokes get accessor int j = names.Capacity; // Invokes get accessor 索引器 MyList<string> names = new MyList<string>(); names.Add("Liz"); names.Add("Martha"); names.Add("Beth"); for (int i = 0; i < names.Count; i++) { string s = names[i]; names[i] = s.ToUpper(); } 事件 过,与字段不同的是,属性不指明存储位置。 相反,属性包含访问器,用于指定在读取或写入属性值时执行的语 句。 属性的声明方式与字段相似,区别是属性声明以在分隔符 { 和 } 之间写入的 get 访问器或 set 访问器结束,而 不是以分号结束。 同时具有 get 访问器和 set 访问器的属性是“读写属性”。 只有 get 访问器的属性是“只读属 性”。 只有 set 访问器的属性是“只写属性”。 get 访问器对应于包含属性类型的返回值的无参数方法。 set 访问器对应于包含一个名为 value 的参数但不含返 回类型的方法。 get 访问器会计算属性的值。 set 访问器会为属性提供新值。 当属性是赋值的目标,或者是 ++ 或 -- 的操作数时,会调用 set 访问器。 在引用了属性的其他情况下,会调用 get 访问器。 MyList<T> 类声明以下两个属性: Count 和 Capacity (分别为只读和读写)。 以下示例代码展示了如何使用这些 属性: 类似于字段和方法,C# 支持实例属性和静态属性。 静态属性使用静态修饰符进行声明,而实例属性则不使用静 态修饰符进行声明。 属性的访问器可以是虚的。 如果属性声明包含 virtual 、 abstract 或 override 修饰符,则适用于属性的访问 器。 借助 索引器 成员,可以将对象编入索引(像处理数组一样)。 索引器的声明方式与属性类似,不同之处在于,索 引器成员名称格式为 this 后跟在分隔符 [ 和 ] 内写入的参数列表。 这些参数在索引器的访问器中可用。 类 似于属性,索引器分为读写、只读和只写索引器,且索引器的访问器可以是虚的。 MyList<T> 类声明一个需要使用 int 参数的读写索引器。 借助索引器,可以使用 int 值将 MyList<T> 实例编 入索引。 例如: 索引器可被重载。 一个类可声明多个索引器,只要其参数的数量或类型不同即可。 借助 事件 成员,类或对象可以提供通知。 事件的声明方式与字段类似,区别是事件声明包括 event 关键字,且 类型必须是委托类型。 在声明事件成员的类中,事件的行为与委托类型的字段完全相同(前提是事件不是抽象的,且不声明访问器)。 字 段存储对委托的引用,委托表示已添加到事件的事件处理程序。 如果没有任何事件处理程序,则字段为 null 。 MyList<T> 类声明一个 Changed 事件成员,指明已向列表添加了新项。 Changed 事件由 OnChanged 虚方法引 发,此方法会先检查事件是否是 null (即不含任何处理程序)。 引发事件的概念恰恰等同于调用由事件表示的委 托。 不存在用于引发事件的特殊语言构造。 客户端通过 事件处理程序 响应事件。 使用 += 和 -= 运算符分别可以附加和删除事件处理程序。 下面的示例