(1) To OOP or Not to OOP
三种解决方案:
完全OOP:
任何事物都是对象,大概会有1000多个对象存储在RAM
例如: Customers, Products, Orders, Addresses, …
OOP的巨大能量能处理任何的问题,但也带来一定的压力。
大颗粒(Large-grain) OOP:
任何事物都是对象,但是对象是大颗粒的。
少量的全面的对象,虽然不优雅,但是更实际。
不使用OOP:
没有对象—返回过程样式的编程
没有类—基于结构和静态方法
例子:大颗粒设计
我们不要把所有的customers都存储在
RAM中。
通过ID获得唯一的customer。
可以查找customer IDs的子集。
namespace BusinessTier
{
public class Customers
{
public Customer Get(int customerID)
{ ... }
public int[] Search(...)
{ ... }
namespace BusinessTier
{
public class Customer
{
...
}
(2) 安全性
不幸的是,安全性非常的复杂,不是一次
Webcast就能说清楚的。
安全性对于任何一层的设计都有潜在的影响
谁将使用本系统?(验证)
是否允许该用户进行此操作?(权限)
在何处进行安全检查?
商务逻辑层
表现层是否需要反映出用户的权限?
数据层是否需要进行检查?
最基本的,通过商务规则来进行检验……
(3) 商务规则
典型地通过明确地编码表现
不同的商务情况有不同的规则
Does customer have a credit limit?
Does customer meet minimum age requirements?
Does user have permission to make this change?
…
但,有一条通用的规则— (校验)validation
正则表达式
REs 是强大的检验技术
过程:
有效的输入被表现为一种模式
对于用户的输入,运行模式进行检验
例如:
“\d” 指定单一的数字{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
“\w+@\w+” 是一个简单的Email地址模式。(1个或多个字符加上
@再加上一个或多个字符)
例子
检验Zipcode是否符合US的标准
Zip or Zip+4
模式:^ 表示开始, ? 可选, $ 结束
using RE = System.Text.RegularExpressions.Regex;
public class Validation
{
public static bool isUSZipcode(string s)
{
string pattern;
pattern = @"^(\d{5})((\-\d{4})?)$";
return RE.Match(s, pattern).Success;
}
强类型DataSets
强类型dataset 是特殊化的DataSet
强类型DataSet是从DataSet继承的定制对象,它可以通
过其显露的属性(properties)来对封装数据进行强类型
的访问。
强类型DataSet避免了字段的晚绑定。强类型DataSet提
供了强类型的访问器,因为避免了到一个集合中查找列名
或表名,访问时更快。
除了能够提高运行时的性能,强类型DataSet还提供了类
型检查,并且在设计时可以通过自定义字段名对字段智能
感知。
例子
用户数据的例子
System.Data.DataSet DS;
DataAccessTier.CustomersDataSet customersDS; ...
// Fill and access a standard DataSet:
dataAdapter.Fill(DS);
FirstName = DS.Tables[“Customers”].Rows[i][“FirstName”].ToString();
// Filling and accessing a typed DataSet:
dataAdapter.Fill(customersDS);
FirstName = customersDS.Customers[i].FirstName;
System.Data.DataSet DataAccessTier.CustomersDataSet ...
// Fill and access a standard DataSet:
DS.Tables[“Customers”].Rows[i][“FirstName”].ToString();
// Filling and accessing a typed DataSet:
customersDS.Customers[i].FirstName;
public class CustomersDataSet : System.Data.DataSet
{
...
}
组件
什么是组件?
组件== 编译后实体
在.NET中,组件== Assembly (exe / .dll)
组件与物理打包相关……
Front-end DB
Business
Tier
Data Access
Tier
BBuussiinneessss..ddllll
GUII..eexxee
DDaattaa..ddllll
谁来控制版本?
你来!
你来决定何时应该改变版本号(任何时候)
你需要保证在不同版本中的兼容性(如果需要)
默认情况下,.NET & CLR 会忽略版本号
如果.EXE 需要.DLL,那么任何版本的.DLL都可以
如果组件有强命名/强名称( strong name ),
那么
如果.EXE 提及需要1.0.3.12 版本的.DLL,那么它必须查
找到1.0.3.12 版本的.DLL…
强命名(Strong Name)?
Assembly 在.NET 的命名有四部分:
friendly, human readable name
culture
version #
public key token
Assembly 如果有公钥token,那么它有强命
名——这表示它被私钥数字签名过
强命名的好处?
安全性,组件无法篡改!
组件的多个版本可以共存!
应用程序可以使用它建立时的组件版本,而不是第一个找
到的DLL
4步用来强命名
创建强命名组件的步骤:
1. 生成一组public-private key pair
2. 将其通过AssemblyInfo.cs 的属性应用到组件中。
3. 重新编译assembly
4. 重新编译客户端
建立Public/Private Key 文件
使用.NET “SN” 命令行用法
-k 选项生成key pair
把该文件放置在VS solution / project目录下
保护Key 文件!
私钥key 的安全-recompile
必须保证key 文件是秘密的
每个程序员是否需要一份key文件的拷贝?
可能不会...
因为.NET 支持后签名:
在发布之前,部署团队签署assembly …
VS .NET中基于组件的应用程序
两种方法来开发基于组件的应用程序:
1. 单一的解决方案中包含多个项目— 这是最简单的方法,
如果你有所有的源代码
2. 多个解决方案,每个都含有一个项目—典型地在一个大
型的项目中或者你没有所有的源代码
版本转向
应用程序的.config 文件将覆盖.EXE 的
manifest
例:
.EXE 引用1.0.0.0 Business Tier
我们要求.EXE 使用2.0.0.0
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="BusinessComponent"
publicKeyToken="1234567890abcdef" />
<bindingRedirect oldVersion="1.0.0.0“ newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
安装到GAC
如何把组件放置进GAC中?
组件必须是强命名的
使用.NET “gacutil” 命令行工具:
优势?
共享DLLs
并行安装不同版本
更快的下载时间(在安装时刻,组件完整的检查)
编码习惯
1. 命名规范,接口用I开头做前缀, 异常类使用Exception作为其后缀。
public class MyClass
{
int myNum=0
public MyMethod(int refObject){}
}
interface IMyInterface
{...}
public class MyEventException
{…}
2. 使用有意义的变量名称和名称空间。有返回值的方法GetMyObjectState()。
3. 所有的成员变量都应该声明在顶部,同时使用一个空行来将他们和属性以及方法
分开。
4. 总是将大括号放在一个新行上。
委托Delegate (1)
想象成C++中的函数指针,但不同点在于
delegate完全面向对象的——既封装方法
又封装对象实例(历史)
定义委托实际上是定义一个类型的委托,
不是一个具体的实例。
委托类型指定它代表的方法的返回类型和
参数表
它代表具有相同参数列表和返回类型的任
何方法
委托Delegate (2)
<modifiers> delegate <return_type> <delegate_name> ( argument_list )
public delegate bool ProcessAnything(double d);
创建委托实例——new关键字
ProcessAnything pa = new
ProcessAnything(account.Withdraw);
括号里面是实例方法,此方法必须和代理声明时的返回类
型和参数列表相同
委托Delegate (3)
委托的调用时通过输入委托实例的名称和
要传递给委托所表示的方法的参数
public class MoneyCompute
{
public static double Compute(double t, DelegateCompute dc)
{
return dc(t);
}
}
public delegate double DelegateCompute(double x);
实例
using System;
using System.Threading;
public class Student
{
public delegate string AdviseDelegate(int score);
public AdviseDelegate AdviseDelegateInstance;
private int score;
public void SetScore(int value)
{
if (value > 100 || value < 0)
{
Console.Out.WriteLine(“Wrong");
}
else
{
score = value;
if (AdviseDelegateInstance!= null)
{
string result=AdviseDelegateInstance(score);
Console.Out.WriteLine(“Result"+result);
}
}
}
实例
public class Teacher
{
public string Advise(int score)
{
if(score<60)
{
Console.Out.WriteLine(score+“Add Oil");
return “Not Pass…";
}
else
{
Console.Out.WriteLine(score+“Good not Perfect");
return “Pass!";
}
}
}
实例
class MainClass
{
[STAThread]
static void Main(string[] args)
{
Teacher teacher=new Teacher();
Student s=new Student();
s.AdviseDelegateInstance=new Student.AdviseDelegate(teacher.Advise);
Console.Out.WriteLine(“Student got 49");
s.SetScore(49);
Console.Out.WriteLine(“Student got 87");
s.SetScore(87);
Console.ReadLine();
}
}
Equals()
考虑覆写Equals(object obj)
.NET 提供了默认的实现方法(引用指向同一对象)
.NET 将在比较和查询时使用这个方法
实现自己比较的“相等”定义,你需要覆写这个方法。
BankCustomer bc1, bc2; ...
if (bc1.Equals(bc2))
...;
...
bc1.Equals(bc2))
ArrayList customers;
customers = new ArrayList(); ...
// we are looking for a particular customer…
BankCustomer bc = ...;
if (customers.IndexOf(bc) >= 0) // found it!
...;
自定义Equals
如果两个客户姓名相等,我们认为对象是相等的
public class BankCustomer
{
public string FirstName;
public string LastName; ...
public override bool Equals(object obj)
{
if (obj == null)
return false;
if ((obj.GetType().Equals(this.GetType())) == false)
return false;
BankCustomer other;
other = (BankCustomer) obj;
return this.FirstName.Equals(other.FirstName) &&
this.LastName.Equals(other.LastName);
}
GetHashCode()
如果覆写了Equals,也需要覆写
GetHashCode (警告)
如果两个对象Equals, GHC 必须返回相同的值
根据Equals定义字段来定义GHC
public class BankCustomer
{
public string FirstName;
public string LastName; ...
public override int GetHashCode()
{
return this.FirstName.GetHashCode() + this.LastName.GetHashCode();
}
Hashtable
Hashtable 提供高效的搜索
基于(key, value) pairs (键/值)对— 键必须是唯一
的
可以通过搜索key来查找
using SC = System.Collections;
SC.Hashtable customers;
customers = new SC.Hashtable();
customers.Add( 12345, new BankCustomer() ); // key, value
customers.Add( 67890, new BankCustomer() ); ...
BankCustomer c;
c = (BankCustomer) customers[67890]; // search by key
foreach (SC.DictionaryEntry hte in customers)
((BankCustomer)hte.Value).Balance += 250.00M;
IDisposable
一些对象需要清理:
Dispose()
类中是否“抓住”资源?
如果是:
a) 重新设计类
b) 实现IDisposable接口,并提供Dispose & Close 方法
解决方案
加入Dispose:
public class Logger : IDisposable
{
private System.IO.StreamWriter logfile;
public Logger(string filename) // constructor
{ this.logfile = new System.IO.StreamWriter(filename, true); }
public void LogIt(string msg, string filename)
{ logfile.WriteLine(msg); }
public void Dispose()
{ logfile.Close(); }
}
public class Logger {
private System.IO.StreamWriter logfile;
public Logger(string filename) // constructor
{ this.logfile = new System.IO.StreamWriter(filename, true); }
public void LogIt(string msg, string filename)
{ logfile.WriteLine(msg); }
Dispose()
}
ICloneable
如果需要实现克隆功能,那么要实现
ICloneable 接口
实现clone方法
你需要确定:浅表复制还是深复制
BankCustomer c;
c = new BankCustomer(“Kim”, “Lee”, 1000.00M);
BankCustomer c2;
c2 = c.Clone();
= c.Clone();
Kim
Lee
1000.00
Kim
Lee
1000.00
你需要确定:浅表复制还是深复制
Shallow vs. Deep
shallow —— 复制顶级(top-level)对象
deep 复制对象和子对象
参看class doc
BankCustomer[] customers;
...
BankCustomer[] shallow;
shallow = customers.Clone();
BankCustomer[] deep;
deep = customers.Clone();
例子
Shallow copies of BankCustomer objects:
相当于:
public class BankCustomer : ICloneable
{ ...
public object Clone()
{
return new BankCustomer(this.FirstName, this.LastName, this.Balance);
}
public class BankCustomer { ...
Clone()
public class BankCustomer : ICloneable
{ ...
public object Clone()
{
return this.MemberwiseClone(); // creates shallow copy for you
}
String相关知识
string是不可变的对象。
字符串连接操作并不更改当前字符串,只是创建并返回新的字符串,速
度慢。
StringBuilder的字符串连接
频繁进行字符串连接操作时,使用StringBuilder类来改善性能,连接操
作越频繁,差别越明显。
字符串驻留
public static Intern(String str)
public static isIntern(String str)
三种解决方案:
完全OOP:
任何事物都是对象,大概会有1000多个对象存储在RAM
例如: Customers, Products, Orders, Addresses, …
OOP的巨大能量能处理任何的问题,但也带来一定的压力。
大颗粒(Large-grain) OOP:
任何事物都是对象,但是对象是大颗粒的。
少量的全面的对象,虽然不优雅,但是更实际。
不使用OOP:
没有对象—返回过程样式的编程
没有类—基于结构和静态方法
例子:大颗粒设计
我们不要把所有的customers都存储在
RAM中。
通过ID获得唯一的customer。
可以查找customer IDs的子集。
namespace BusinessTier
{
public class Customers
{
public Customer Get(int customerID)
{ ... }
public int[] Search(...)
{ ... }
namespace BusinessTier
{
public class Customer
{
...
}
(2) 安全性
不幸的是,安全性非常的复杂,不是一次
Webcast就能说清楚的。
安全性对于任何一层的设计都有潜在的影响
谁将使用本系统?(验证)
是否允许该用户进行此操作?(权限)
在何处进行安全检查?
商务逻辑层
表现层是否需要反映出用户的权限?
数据层是否需要进行检查?
最基本的,通过商务规则来进行检验……
(3) 商务规则
典型地通过明确地编码表现
不同的商务情况有不同的规则
Does customer have a credit limit?
Does customer meet minimum age requirements?
Does user have permission to make this change?
…
但,有一条通用的规则— (校验)validation
正则表达式
REs 是强大的检验技术
过程:
有效的输入被表现为一种模式
对于用户的输入,运行模式进行检验
例如:
“\d” 指定单一的数字{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
“\w+@\w+” 是一个简单的Email地址模式。(1个或多个字符加上
@再加上一个或多个字符)
例子
检验Zipcode是否符合US的标准
Zip or Zip+4
模式:^ 表示开始, ? 可选, $ 结束
using RE = System.Text.RegularExpressions.Regex;
public class Validation
{
public static bool isUSZipcode(string s)
{
string pattern;
pattern = @"^(\d{5})((\-\d{4})?)$";
return RE.Match(s, pattern).Success;
}
强类型DataSets
强类型dataset 是特殊化的DataSet
强类型DataSet是从DataSet继承的定制对象,它可以通
过其显露的属性(properties)来对封装数据进行强类型
的访问。
强类型DataSet避免了字段的晚绑定。强类型DataSet提
供了强类型的访问器,因为避免了到一个集合中查找列名
或表名,访问时更快。
除了能够提高运行时的性能,强类型DataSet还提供了类
型检查,并且在设计时可以通过自定义字段名对字段智能
感知。
例子
用户数据的例子
System.Data.DataSet DS;
DataAccessTier.CustomersDataSet customersDS; ...
// Fill and access a standard DataSet:
dataAdapter.Fill(DS);
FirstName = DS.Tables[“Customers”].Rows[i][“FirstName”].ToString();
// Filling and accessing a typed DataSet:
dataAdapter.Fill(customersDS);
FirstName = customersDS.Customers[i].FirstName;
System.Data.DataSet DataAccessTier.CustomersDataSet ...
// Fill and access a standard DataSet:
DS.Tables[“Customers”].Rows[i][“FirstName”].ToString();
// Filling and accessing a typed DataSet:
customersDS.Customers[i].FirstName;
public class CustomersDataSet : System.Data.DataSet
{
...
}
组件
什么是组件?
组件== 编译后实体
在.NET中,组件== Assembly (exe / .dll)
组件与物理打包相关……
Front-end DB
Business
Tier
Data Access
Tier
BBuussiinneessss..ddllll
GUII..eexxee
DDaattaa..ddllll
谁来控制版本?
你来!
你来决定何时应该改变版本号(任何时候)
你需要保证在不同版本中的兼容性(如果需要)
默认情况下,.NET & CLR 会忽略版本号
如果.EXE 需要.DLL,那么任何版本的.DLL都可以
如果组件有强命名/强名称( strong name ),
那么
如果.EXE 提及需要1.0.3.12 版本的.DLL,那么它必须查
找到1.0.3.12 版本的.DLL…
强命名(Strong Name)?
Assembly 在.NET 的命名有四部分:
friendly, human readable name
culture
version #
public key token
Assembly 如果有公钥token,那么它有强命
名——这表示它被私钥数字签名过
强命名的好处?
安全性,组件无法篡改!
组件的多个版本可以共存!
应用程序可以使用它建立时的组件版本,而不是第一个找
到的DLL
4步用来强命名
创建强命名组件的步骤:
1. 生成一组public-private key pair
2. 将其通过AssemblyInfo.cs 的属性应用到组件中。
3. 重新编译assembly
4. 重新编译客户端
建立Public/Private Key 文件
使用.NET “SN” 命令行用法
-k 选项生成key pair
把该文件放置在VS solution / project目录下
保护Key 文件!
私钥key 的安全-recompile
必须保证key 文件是秘密的
每个程序员是否需要一份key文件的拷贝?
可能不会...
因为.NET 支持后签名:
在发布之前,部署团队签署assembly …
VS .NET中基于组件的应用程序
两种方法来开发基于组件的应用程序:
1. 单一的解决方案中包含多个项目— 这是最简单的方法,
如果你有所有的源代码
2. 多个解决方案,每个都含有一个项目—典型地在一个大
型的项目中或者你没有所有的源代码
版本转向
应用程序的.config 文件将覆盖.EXE 的
manifest
例:
.EXE 引用1.0.0.0 Business Tier
我们要求.EXE 使用2.0.0.0
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="BusinessComponent"
publicKeyToken="1234567890abcdef" />
<bindingRedirect oldVersion="1.0.0.0“ newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
安装到GAC
如何把组件放置进GAC中?
组件必须是强命名的
使用.NET “gacutil” 命令行工具:
优势?
共享DLLs
并行安装不同版本
更快的下载时间(在安装时刻,组件完整的检查)
编码习惯
1. 命名规范,接口用I开头做前缀, 异常类使用Exception作为其后缀。
public class MyClass
{
int myNum=0
public MyMethod(int refObject){}
}
interface IMyInterface
{...}
public class MyEventException
{…}
2. 使用有意义的变量名称和名称空间。有返回值的方法GetMyObjectState()。
3. 所有的成员变量都应该声明在顶部,同时使用一个空行来将他们和属性以及方法
分开。
4. 总是将大括号放在一个新行上。
委托Delegate (1)
想象成C++中的函数指针,但不同点在于
delegate完全面向对象的——既封装方法
又封装对象实例(历史)
定义委托实际上是定义一个类型的委托,
不是一个具体的实例。
委托类型指定它代表的方法的返回类型和
参数表
它代表具有相同参数列表和返回类型的任
何方法
委托Delegate (2)
<modifiers> delegate <return_type> <delegate_name> ( argument_list )
public delegate bool ProcessAnything(double d);
创建委托实例——new关键字
ProcessAnything pa = new
ProcessAnything(account.Withdraw);
括号里面是实例方法,此方法必须和代理声明时的返回类
型和参数列表相同
委托Delegate (3)
委托的调用时通过输入委托实例的名称和
要传递给委托所表示的方法的参数
public class MoneyCompute
{
public static double Compute(double t, DelegateCompute dc)
{
return dc(t);
}
}
public delegate double DelegateCompute(double x);
实例
using System;
using System.Threading;
public class Student
{
public delegate string AdviseDelegate(int score);
public AdviseDelegate AdviseDelegateInstance;
private int score;
public void SetScore(int value)
{
if (value > 100 || value < 0)
{
Console.Out.WriteLine(“Wrong");
}
else
{
score = value;
if (AdviseDelegateInstance!= null)
{
string result=AdviseDelegateInstance(score);
Console.Out.WriteLine(“Result"+result);
}
}
}
实例
public class Teacher
{
public string Advise(int score)
{
if(score<60)
{
Console.Out.WriteLine(score+“Add Oil");
return “Not Pass…";
}
else
{
Console.Out.WriteLine(score+“Good not Perfect");
return “Pass!";
}
}
}
实例
class MainClass
{
[STAThread]
static void Main(string[] args)
{
Teacher teacher=new Teacher();
Student s=new Student();
s.AdviseDelegateInstance=new Student.AdviseDelegate(teacher.Advise);
Console.Out.WriteLine(“Student got 49");
s.SetScore(49);
Console.Out.WriteLine(“Student got 87");
s.SetScore(87);
Console.ReadLine();
}
}
Equals()
考虑覆写Equals(object obj)
.NET 提供了默认的实现方法(引用指向同一对象)
.NET 将在比较和查询时使用这个方法
实现自己比较的“相等”定义,你需要覆写这个方法。
BankCustomer bc1, bc2; ...
if (bc1.Equals(bc2))
...;
...
bc1.Equals(bc2))
ArrayList customers;
customers = new ArrayList(); ...
// we are looking for a particular customer…
BankCustomer bc = ...;
if (customers.IndexOf(bc) >= 0) // found it!
...;
自定义Equals
如果两个客户姓名相等,我们认为对象是相等的
public class BankCustomer
{
public string FirstName;
public string LastName; ...
public override bool Equals(object obj)
{
if (obj == null)
return false;
if ((obj.GetType().Equals(this.GetType())) == false)
return false;
BankCustomer other;
other = (BankCustomer) obj;
return this.FirstName.Equals(other.FirstName) &&
this.LastName.Equals(other.LastName);
}
GetHashCode()
如果覆写了Equals,也需要覆写
GetHashCode (警告)
如果两个对象Equals, GHC 必须返回相同的值
根据Equals定义字段来定义GHC
public class BankCustomer
{
public string FirstName;
public string LastName; ...
public override int GetHashCode()
{
return this.FirstName.GetHashCode() + this.LastName.GetHashCode();
}
Hashtable
Hashtable 提供高效的搜索
基于(key, value) pairs (键/值)对— 键必须是唯一
的
可以通过搜索key来查找
using SC = System.Collections;
SC.Hashtable customers;
customers = new SC.Hashtable();
customers.Add( 12345, new BankCustomer() ); // key, value
customers.Add( 67890, new BankCustomer() ); ...
BankCustomer c;
c = (BankCustomer) customers[67890]; // search by key
foreach (SC.DictionaryEntry hte in customers)
((BankCustomer)hte.Value).Balance += 250.00M;
IDisposable
一些对象需要清理:
Dispose()
类中是否“抓住”资源?
如果是:
a) 重新设计类
b) 实现IDisposable接口,并提供Dispose & Close 方法
解决方案
加入Dispose:
public class Logger : IDisposable
{
private System.IO.StreamWriter logfile;
public Logger(string filename) // constructor
{ this.logfile = new System.IO.StreamWriter(filename, true); }
public void LogIt(string msg, string filename)
{ logfile.WriteLine(msg); }
public void Dispose()
{ logfile.Close(); }
}
public class Logger {
private System.IO.StreamWriter logfile;
public Logger(string filename) // constructor
{ this.logfile = new System.IO.StreamWriter(filename, true); }
public void LogIt(string msg, string filename)
{ logfile.WriteLine(msg); }
Dispose()
}
ICloneable
如果需要实现克隆功能,那么要实现
ICloneable 接口
实现clone方法
你需要确定:浅表复制还是深复制
BankCustomer c;
c = new BankCustomer(“Kim”, “Lee”, 1000.00M);
BankCustomer c2;
c2 = c.Clone();
= c.Clone();
Kim
Lee
1000.00
Kim
Lee
1000.00
你需要确定:浅表复制还是深复制
Shallow vs. Deep
shallow —— 复制顶级(top-level)对象
deep 复制对象和子对象
参看class doc
BankCustomer[] customers;
...
BankCustomer[] shallow;
shallow = customers.Clone();
BankCustomer[] deep;
deep = customers.Clone();
例子
Shallow copies of BankCustomer objects:
相当于:
public class BankCustomer : ICloneable
{ ...
public object Clone()
{
return new BankCustomer(this.FirstName, this.LastName, this.Balance);
}
public class BankCustomer { ...
Clone()
public class BankCustomer : ICloneable
{ ...
public object Clone()
{
return this.MemberwiseClone(); // creates shallow copy for you
}
String相关知识
string是不可变的对象。
字符串连接操作并不更改当前字符串,只是创建并返回新的字符串,速
度慢。
StringBuilder的字符串连接
频繁进行字符串连接操作时,使用StringBuilder类来改善性能,连接操
作越频繁,差别越明显。
字符串驻留
public static Intern(String str)
public static isIntern(String str)
浙公网安备 33010602011771号