UML软件工程组织

我们一起来进入C# 2.0的时代
作者:徐志平
理解C# 2.0 的可空类型

以下代码是不允许的
static void Main(string[] args)
{
// 编译器错误!
// 数值不能设定为 null!
bool myBool = null;
int myInt = null;
}
但是如果一定要定义一个可空类型呢?
static void Main(string[] args)
{
// 定义局部的可空类型,在类型后面加一个?
int? nullableInt = 10;
double? nullableDouble = 3.14;
bool? nullableBool = null;
char? nullableChar = 'a';
int?[] arrayOfNullableInts = new int?[10];
// 错误! String s 为引用类型!
string? s = "oops";
}
在C#中,?实际上为System.Nullable<T>构造的缩写。System.Nullable<T>提供了所有能够可以空值的数据类型。
使用可空值类型的数据的方法如下,请注意“?”符号
class DatabaseReader
{

// 可空数据字段
public int? numbericValue;
public bool? boolValue = true;

// 注意到了可空返回类型了么?
public int? GetIntFromDatabase()
{ return numbericValue; }

public bool? GetBoolFromDatabase()
{ return boolValue; }
}

s static void Main(string[] args)
{
Console.WriteLine("***** 可空数据类型 *****\n");
DatabaseReader dr = new DatabaseReader();
// 读取信息
int? i = dr.GetIntFromDatabase();
if (i.HasValue)
Console.WriteLine("'i' 的值: {0}", i);
else
Console.WriteLine("'i' 的值未定义.");

bool? b = dr.GetBoolFromDatabase();
if (b != null)
Console.WriteLine("'b' 的值: {0}", b);
else
Console.WriteLine("'b' 的值未定义.");
Console.ReadLine();
}

??操作符
这个是C# 2.0所特有的操作符,看了下面的例子,你应该明白其作用了:
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Nullable Data *****\n");
DatabaseReader dr = new DatabaseReader();
...

int? myData = dr.GetIntFromDatabase() ?? 100;
Console.WriteLine("Value of myData: {0}", myData);
Console.ReadLine();
}

数组范例


using System;

namespace Arrays
{
class Program
{
#region 辅助函数
public static void PrintArray(int[] myInts)
{
for (int i = 0; i < myInts.Length; i++)
Console.WriteLine("项目 {0} 为 {1}", i, myInts[i]);
}

public static string[] GetStringArray()
{
string[] theStrings = { "从", "GetStringArray","说你好" };
return theStrings;
}
#endregion

static void Main(string[] args)
{
Console.WriteLine("***** 数组范例 *****\n");

#region Array declarations

// 含有3个元素的数组.
string[] booksOnCOM= new string[3];
booksOnCOM[0] = "Developer’s Workshop to COM and ATL 3.0";
booksOnCOM[1] = "Inside COM";
booksOnCOM[2] = "Inside ATL";
foreach (string s in booksOnCOM)
Console.WriteLine(s);

//初始化100个元素,从0开始计数
string [] booksOnDotNet = new string [100];

// 不同的建立相

同大小的数组的方法
int[] n = new int[] { 20, 22, 23, 0 };
int[] n2 = new int[4] { 20, 22, 23, 0 };
int[] n3 = { 20, 22, 23, 0 };
#endregion

Console.WriteLine("\n***** 数组作为参数 *****");
PrintArray(n2);

Console.WriteLine("\n***** 数组作为返回值 *****");
string[] strs = GetStringArray();
foreach (string s in strs)
Console.WriteLine(s);

#region MD 数组
Console.WriteLine("\n***** MD 数组 *****\n");
int[,] myMatrix;
myMatrix = new int[6, 6];

for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
myMatrix[i, j] = i * j;

for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
Console.Write(myMatrix[i, j] + "\t");
}
Console.WriteLine();
}

Console.WriteLine("\n***** 锯齿数组 *****\n");
int[][] myJagArray = new int[5][];

// 构造锯齿数组
for (int i = 0; i < myJagArray.Length; i++)
myJagArray[i] = new int[i + 7];

// 打印每一行,每一个元素的缺省值为0
for (int i = 0; i < 5; i++)
{
Console.Write("行 {0} 的长度 {1}:\t", i, myJagArray[i].Length);
for (int j = 0; j < myJagArray[i].Length; j++)
Console.Write(myJagArray[i][j] + " ");
Console.WriteLine();
}
#endregion

Console.WriteLine("\n***** 构造字符串数组 *****");
string[] firstNames = { "毛泽东", "周恩来", "朱德", "陈毅" };
Console.WriteLine("开国元勋:");
for (int i = 0; i < firstNames.Length; i++)
Console.Write("名字: {0}\t", firstNames[i]);
Console.WriteLine("\n");
Array.Reverse(firstNames);
Console.WriteLine("数组逆序:");
for (int i = 0; i < firstNames.Length; i++)
Console.Write("名字: {0}\t", firstNames[i]);
Console.WriteLine("\n");

Array.Clear(firstNames, 1, 3);
for (int i = 0; i < firstNames.Length; i++)
Console.Write("名字: {0}\t", firstNames[i]);
Console.ReadLine();
}
}
}

迭代构造


// 基本的 for 循环.
static void Main(string[] args)
{
// 注意! 'i' 仅在for循环范围可见
for(int i = 0; i < 10; i++)
{
Console.WriteLine("数字: {0} ", i);
}
// 'i' 在此处不可见.
}

// 使用 foreach 迭代数组内容.
static void Main(string[] args)
{
string[] books = {"复杂算法",
"COM本质",
"C# and the .NET Platform"};
foreach (string s in books)
Console.WriteLine(s);
int[] myInts = { 10, 20, 30, 40 };
foreach (int i in myInts)
Console.WriteLine(i);
}

while循环
static void Main(string[] args)
{
string userIsDone = "no";

while(userIsDone.ToLower() != "yes")
{
Console.Write("完成否? [yes] [no]: ");
userIsDone = Console.ReadLine();
Console.WriteLine("In while loop");
}
}

static void Main(string[] args)
{
string userIsDone = "";
do
{
Console.WriteLine("In do/while loop");
Console.Write("完成否? [yes] [no]: ");
userIsDone = Console.ReadLine();
}while(userIsDone.ToLower() != "yes"); // Note the semicolon!
}

C#的方法的参数修饰符详解


方法参数修饰
(啥也没有) 如果参数的修饰是啥也没有,那么其参数传递的方式是值传递,接受方收到的是原始数据的拷贝
out 说明了参数是引用传递。
params 可变参,注意了这种修饰符针对的参数一定是最后一个参数
ref 引用传递,参数的内容会改变。


// 缺省是传值
public static int Add(int x, int y)
{
int ans = x + y;
x = 10000;
y = 88888;
return ans;
}


static void Main(string[] args)
{
int x = 9, y = 10;
Console.WriteLine("调用前: X: {0}, Y: {1}", x, y);
Console.WriteLine("结果: {0}", Add(x, y));
Console.WriteLine("调用后: X: {0}, Y: {1}", x, y);
}


// 输出修饰
public static void Add(int x, int y, out int ans)
{
ans = x + y;
}


static void Main(string[] args)
{
// 不需要进行本地赋值
int ans;
Add(90, 90, out ans);
Console.WriteLine("90 + 90 = {0} ", ans);
}


// 多个输出修饰
public static void FillTheseValues(out int a, out string b, out bool c)
{
a = 9;
b = "Enjoy your string.";
c = true;
}


static void Main(string[] args)
{
int i;
string str;
bool b;
FillTheseValues(out i, out str, out b);
Console.WriteLine("Int is: {0}", i);
Console.WriteLine("String is: {0}", str);
Console.WriteLine("Boolean is: {0}", b);
}


//引用修饰
public static void SwapStrings(ref string s1, ref string s2)
{
string tempStr = s1;
s1 = s2;
s2 = tempStr;
}
This method can be called as so:
static void Main(string[] args)
{
string s = "第一个字符串";
string s2 = "其他字符串";
Console.WriteLine("之前: {0}, {1} ", s, s2);
SwapStrings(ref s, ref s2);
Console.WriteLine("之后: {0}, {1} ", s, s2);
}


//可变参
static double CalculateAverage(params double[] values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
sum += values[i];
return (sum / values.Length);
}


static void Main(string[] args)
{
// Pass in a comma-delimited list of doubles...
double average;
average = CalculateAverage(4.0, 3.2, 5.7);
Console.WriteLine("4.0, 3.2, 5.7 的平均数是: {0}",
average);
double[] data = { 4.0, 3.2, 5.7 };
average = CalculateAverage(data);
Console.WriteLine("Average of data is: {0}", average);
Console.ReadLine();
}


C# 2.0的静态类


静态类是C# 2.0中特有的,一旦一个类被定义成为静态的,其不能使用new关键字构造,其只能包含静态成员或者字段,否则编译器会报错!
static class UtilityClass
{
public static void PrintTime()
{ Console.WriteLine(DateTime.Now.ToShortTimeString()); }
public static void PrintDate()
{ Console.WriteLine(DateTime.Today.ToShortDateString()); }
}
以下代码会出错!
static void Main(string[] args)
{
UtilityClass.PrintDate();
// Compiler error! Can't create static classes.
UtilityClass u = new UtilityClass();
...
}
回想早期的C#中如果需要阻止类的构造,有以下两种方法
方法1:
class UtilityClass
{
private UtilityClass(){}
...
}
方法2:
abstract class UtilityClass
{
...
}
不过这两种方法都存在着类型安全的隐患,C# 2.0的方法应该是不错的选择


一些关于静态构造器的说明


一个给定的类(或者结构)只能定义一个静态构造器
静态构造器只执行一次,无论多少个此类的对象被创建
静态构造器不能出现任何的访问修饰符
在运行的时候,静态构造器在存取任何静态成员之前首先被执行
静态构造器在任何实例级别的构造器之前执行


C# 2.0中静态数据的处理


#region Using directives


using System;
using System.Collections.Generic;
using System.Text;


#endregion


namespace StaticData
{
// 静态的类只能够包含静态的方法和常量字段
static class UtilityClass
{
public static void PrintTime()
{ Console.WriteLine(DateTime.Now.ToShortTimeString()); }
public static void PrintDate()
{ Console.WriteLine(DateTime.Today.ToShortDateString()); }
}


#region SavingsAccount Class
class SavingsAccount
{
public SavingsAccount(double balance)
{
currBalance = balance;
}


// 静态构造器
static SavingsAccount()
{
Console.WriteLine("在静态构造器中");
currInterestRate = 0.04;
}


public double currBalance;
public static double currInterestRate;


// 静态方法获得/设定存款利率
public static void SetInterestRate(double newRate)
{ currInterestRate = newRate; }
public static double GetInterestRate()
{ return currInterestRate; }


// 实例方法获得/设定利率
public void SetInterestRateObj(double newRate)
{ currInterestRate = newRate; }
public double GetInterestRateObj()
{ return currInterestRate; }
}
#endregion


class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** 静态数据范例 *****");
UtilityClass.PrintDate();
UtilityClass.PrintTime();


// 以下代码会错误
// UtilityClass u = new UtilityClass();

SavingsAccount s1 = new SavingsAccount(50);
SavingsAccount s2 = new SavingsAccount(100);

// 获得和设定利率
Console.WriteLine("利率为: {0}", s1.GetInterestRateObj());
s2.SetInterestRateObj(0.08);


// 构造新的对象,不会重置利率
SavingsAccount s3 = new SavingsAccount(10000.75);
Console.WriteLine("利率为: {0}", SavingsAccount.GetInterestRate());
Console.WriteLine("利率为: {0}", s3.GetInterestRateObj());
//两个输出依然是0.08
Console.ReadLine();


}
}
}


看了上面的例子,我们可以得知类中的静态数据不是随着每一个实例的构造的时候独享的,而是共享的。改变了一次,其他都会受到波及


C# 2.0中静态方法的有趣范例


#region Using directives


using System;
using System.Collections.Generic;
using System.Text;


#endregion


namespace StaticMethods
{
#region Teenager Class
class Youth
{
private static Random r = new Random();
private static int GetRandomNumber(short upperLimit)
{ return r.Next(upperLimit); }


public static string Complain()
{
string[] messages = new string[5]{ "什么时候买得起房?",
"油价这么贵,还让人活不!", "无聊的电视节目...",
"想自己当老板又没有钱!", "结婚的开销也不小" };
return messages[GetRandomNumber(5)];
}


// OK, Teens are not always upset ;-)
public static string BeAgreeable()
{
string[] messages = new string[3]{ "我比我们老板可以多活20年!",
"两个供房压力会减轻一些", "比贫困地区我们还算过的不错" };
return messages[GetRandomNumber(3)];
}
}
#endregion


class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** 唉,年轻... *****\n");

for (int i = 0; i < 10; i++)
{
Console.WriteLine("抱怨-> {0}", Youth.Complain());
Console.WriteLine("积极的一面-> {0}", Youth.BeAgreeable());
}
Console.ReadLine();
}
}
}


C#中的static关键字的一些概念


static关键字的意义在于其所修饰的方法必须从类级别(Class)而不是对象(Object)级别调用,看一个简单的例子:


由于System.Console中的Writeln是被static所修饰,那么只能通过类级别调用


以下的方法是错误的
Console c = new Console();
c.WriteLine("I can't be printed...");


以下的方法是正确的


Console.WriteLine("Thanks...");


只读属性的例子


#region Using directives


using System;
using System.Collections.Generic;
using System.Text;


#endregion


namespace ReadOnlyFields
{
class Tire
{
public static readonly Tire GoodStone = new Tire(90);
public static readonly Tire FireYear = new Tire(100);


public int manufactureID;


public Tire(){}
public Tire(int ID)
{ manufactureID = ID; }
}


class Employee
{
public readonly string SSN;

public Employee(string empSSN)
{
SSN = empSSN;
}
}


class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** 只读数据 *****");
Tire myTire = Tire.FireYear;
Tire mynewTire = new Tire(1001);
Console.WriteLine("我的轮胎的ID: {0}", myTire.manufactureID);
Console.WriteLine("我的新轮胎的ID: {0}", mynewTire.manufactureID);


Employee e = new Employee("111-22-1111");
// e.SSN = "222-22-2222"; // 这样子会发生错误,因为只读属性不能被修改


Console.ReadLine();
}
}
}


C#中常量的定义和引用的例子


#region Using directives


using System;
using System.Collections.Generic;
using System.Text;


#endregion


namespace Constants
{
static class ConstData
{
// 常量在编译的时候必须被指定
public const string BestNbaTeam = "Timberwolves";
public const double SimplePI = 3.14;
public const bool Truth = true;
public const bool Falsity = !Truth; //逻辑反
}


class Program
{
public const string BestNhlTeam = "Wild";


static void Main(string[] args)
{
Console.WriteLine("***** 常量范例 *****");
// 引用其他的常量
Console.WriteLine("Nba const: {0}", ConstData.BestNbaTeam);
Console.WriteLine("SimplePI const: {0}", ConstData.SimplePI);
Console.WriteLine("Truth const: {0}", ConstData.Truth);
Console.WriteLine("Falsity const: {0}", ConstData.Falsity);


// 打印成员级别的常量


Console.WriteLine("Nhl const: {0}", BestNhlTeam);


// 打印本地范围的常量


const int LocalFixedValue = 4;
Console.WriteLine("Local const: {0}", LocalFixedValue);
Console.ReadLine();
}
}
}


类成员初始化的一些方法


方法一:


class Test
{
public int myInt;
public string myString;
public Test() { myInt = 9; }
public Test(string s)
{
myInt = 9;
myString = s;
}
}


方法二:


class Test
{
public int myInt;
public string myString;
public Test() { InitData(); }
public Test(string s)
{
myString = s;
InitData();
}
private void InitData()
{ myInt = 9; }
}


方法三:


class Test
{
public int myInt = 9;
public string myStr = "My initial value.";
public SportsCar viper = new SportsCar(Color.Red);
...
}


类成员的缺省值


class Test
{
public int myInt; // 设定为0.


public string myString; // 设定为 null.
public bool myBool; // 设定为 false.
public object myObj; // 设定为null.
}


在使用变量之前一定要赋值。


类的可视化的问题


class SomeClass
{
// 处处可访问
public void PublicMethod() { }
// 只能从SomeClass类型访问
private void PrivateMethod() { }
// 只能从 SomeClass 和其子类访问
protected void ProtectedMethod() { }
// 可以从同样的 assembly 中访问
internal void InternalMethod() { }
// Assembly-保护的访问
protected internal void ProtectedInternalMethod() { }
// 缺省的是 private
void SomeMethod() { }
}


控制台格式化输出的例子(2)


方法1


object[] stuff = {"Hello", 20.9, 1, "There", "83", 99.99933} ;
Console.WriteLine("The Stuff: {0} , {1} , {2} , {3} , {4} , {5} ", stuff);


方法2


Console.WriteLine("{0}, Number {0}, Number {0}", 9);


方法3


Console.WriteLine("C format: {0:C}", 99989.987);
Console.WriteLine("D9 format: {0:D9}", 99999);
Console.WriteLine("E format: {0:E}", 99999.76543);
Console.WriteLine("F3 format: {0:F3}", 99999.9999);


Console.WriteLine("N format: {0:N}", 99999);
Console.WriteLine("X format: {0:X}", 99999);
Console.WriteLine("x format: {0:x}", 99999);


方法4


string formatStr;
formatStr =
String.Format("Don't you wish you had {0:C} in your account?",
99989.987);
Console.WriteLine(formatStr);


C#中main的变体


// 不返回类型,一组数组作为参数


public static void Main(string[] args)
{
}


// 不返回类型,无参数

public static void Main()
{
}


// 整数返回类型,无参数
public static int Main()
{
}


Visual Studio 2005 中的OTB(Object Test Bench)


VS2005中一个比较吸引人的地方是OTB(对象测试床),能够使得开发人员在IDE中快速的建立对象的实例并且能够调用对象中的方法


我们有如下的代码


using System;
using System.Collections.Generic;
using System.Text;


namespace ConsoleApplication4
{
class Program
{
public int username
{
get
{
throw new System.NotImplementedException();
}
set
{
}
}


static void Main(string[] args)
{
}


static void Showtext(string atext)
{
Console.WriteLine(atext);
}
}
}


和如下的类关系图:


我们在对象图中选择对象,使用鼠标右键出现


就可以调用我们自己的静态方法了


*注 输入字符串的时候请用" "号包含您要输入的参数的内容


C#中类声明的方法


方法1:


HelloClass c1 = new HelloClass();


方法2:


H elloClass c2;
c2 = new HelloClass();


RSP的简单实例


# External assembly references.
/r:System.Windows.Forms.dll
# output and files to compile (using wildcard syntax).
/target:exe /out:TestApp.exe *.cs


VS 2005 的类的可视化编辑


注意到这个放大镜的图标了么,我们就可以使用它来进行察看类的关系图


这个就是刚才代码的类关系图


Visual Studio 2005 的重构(Refactor) 功能的使用


先输入以下代码


using System;
using System.Collections.Generic;
using System.Text;


namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
// Set up Console UI (CUI)
Console.Title = "My Rocking App";
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Blue;
Console.WriteLine("*************************************");
Console.WriteLine("***** Welcome to My Rocking App *****");
Console.WriteLine("*************************************");
Console.BackgroundColor = ConsoleColor.Black;
// Wait for key press to close.
Console.ReadLine();
}
}
}


选择需要重构的代码,选择“提取方法”


下面就是重构以后的代码了


using System;
using System.Collections.Generic;
using System.Text;


namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
// Set up Console UI (CUI)
ConsoleUI();
// Wait for key press to close.
Console.ReadLine();
}


private static void ConsoleUI()
{
Console.Title = "My Rocking App";
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Blue;
Console.WriteLine("*************************************");
Console.WriteLine("***** Welcome to My Rocking App *****");
Console.WriteLine("*************************************");
Console.BackgroundColor = ConsoleColor.Black;
}
}
}


第一个C# 2.0程序


using System;


namespace CalculatorExample
{
public class CalcApp
{
static void Main()
{
Calc c = new Calc();
int ans = c.Add(10, 84);
Console.WriteLine("10 + 84 is {0}.", ans);
Console.ReadLine();
}
}


public class Calc
{
public int Add(int x, int y)
{
return x + y;
}
}



版权所有:UML软件工程组织