(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)

posted on 2006-08-03 17:22  公木子  阅读(367)  评论(0)    收藏  举报