1. 类的访问修饰符
在C#中,类的访问修饰符有internal和public两种,这里首先要说一下程序集的概念才能继续对访问修饰符的解释。
就我的理解,.NET中程序集的概念类似于Java中的jar包,也是有特定功能程序的集合,不过通常是以扩展名为dll的格式。与jar包相同,我们写的程序中也可以引用微软的或者第三方的程序集来扩展程序的功能。
好了,言归正传,回到类的访问修饰符上。internal代表类只能在程序集内部被其他类访问,这个也是类的默认访问级别。而public代表类可以被其他任意的类访问。
在Java中,类的访问修饰符概念就稍有不同了。对于外部类,只有默认(没有修饰符)和public两个。其中默认修饰符表示可以被相同包中的其他类访问,而public与C#中是类似的。内部类由于C#中并没有对应的概念,就不提它的访问修饰符了。
从这里,可以看出C#和Java的一些不同,C#用程序集来作为internal和public的分界线,而Java则用的包(package)。那么,我可以举一个如下的例子:
{
class Console {
public static void WriteLine(string msg) {
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("111");
}
}
}
这个在编译的时候得到了一个警告,不过我还是替换了System.Console.WriteLine()。在Java中,这个问题就不存在了,比如,你也可以自己覆盖一个String类,并覆盖一系列方法,但是调用的时候一定是Java类库中的String类,因为Java中的ClassLoader机制的存在,这里因为是C#的教程,就不去详细的叙述ClassLoader了。但是C#的这种区分internal和public的方式还是有意义的,因为在Java中,可以通过设定与载入的jar包中的类在相同的包之内来使用一些非API的类,而在C#中这个问题就不存在了,因为API与非API类就是通过程序集来区分的。
另外,通过这个问题,还可以看出Java与C#的package和namespace概念也是有一些不同的,namespace并没有"背负"上那么多责任。
2. 成员的访问修饰符
C#中,成员的访问修饰符有5种,分别是:
- 私有的(private)
- 公有的(public)
- 受保护的(protected)
- 内部的(internal)
- 受保护内部的(protected internal)
C#中成员的访问修饰符也是与程序集有关的。首先我们按照目标类和调用类按照是否在同一个程序集和是否调用类是目标类的子类来分成四种情况。
- 同一个程序集没有继承关系
- 同一个程序集有继承关系
- 不同程序集没有继承关系
- 不同程序集有继承关系
那么,对于不同的访问级别,下面的列表来列出了上面各种情况是否调用类可以访问到目标类的成员
同程序集,无继承 | 同程序集,有继承 | 不同程序集,无继承 | 不同程序集,有继承 | |
---|---|---|---|---|
private | ||||
public | √ | √ | √ | √ |
protected | √ | √ | ||
internal | √ | √ | ||
protected internal | √ | √ | √ |
其中protected internal的概念是protected和internal的"并集"。
3. 本地常量
C#中,有一个const关键字,放在类型名的前面,比如:const int i = 10; 和Java的final变量有些相似,不过const常量是必须初始化的,但是final变量可以不初始化,但是初始化之后就不能改变。
4. 引用参数,输出参数和数组参数
严格的说Java中的参数传递只有值传递和可变参数两种,对象类型的传递其实传递的是对象的地址,也就是引用的值了。
在C#中,参数传递稍微丰富了一些。除了基本的参数传递和Java一样是值传递之外,还有一些它自己的特色。
(1) 引用参数
在方法的参数前加上"ref"关键字那么这个参数就变成引用参数了。
使用引用参数后,不会在栈中为形参分配内存了,而是让形参和实参引用相同的内存位置。举个简单的例子,
{
static void TestMethod(ref int variable)
{
variable += 5;
}
static void Main(string[] args)
{
int i = 0;
TestMethod(ref i);
Console.WriteLine(i);
}
}
这段代码会打印5,因为形参和实参引用相同的那内存。
使用引用参数时,有一点要注意,实参必须是变量的形式,并且必须是已经赋值的,哪怕是null。比如TestMethod(ref 1+1);这种使用方法是不对的,理由也很明显。
(2) 输出参数
在方法的参数前加上"out"关键字就能使这个参数变成输出参数。
输出参数的作用和oracle的PL/SQL存储过程中的out参数的作用比较相似。顾名思义,是用作输出的参数,可以扩展返回值的个数。使用输出参数的时候,有一点与引用参数是一致的,就是实参必须是变量,但是,在调用前不需要为它赋值。因为在方法内部,要求输出参数在被读取之前必须被赋值。所以之前的初始值其实是无所谓的。并且在方法返回之前所有的输出参数都要被赋值。
{
class TestClass
{
public int Property = 0;
}
class Program
{
static void TestMethod(out int out1, out TestClass out2)
{
out1 = 5;
out2 = new TestClass();
out2.Property = 10;
}
static void Main(string[] args)
{
int i;
TestClass c;
TestMethod(out i, out c);
Console.WriteLine(i + ", " +c.Property);
}
}
}
(3) 数组参数
数组参数和Java中的可变参数的概念比较类似,不同的是表示的方法上。
C#使用如下的语法:
{
static void TestMethod(params int[] vars)
{
foreach(int var in vars)
{
Console.WriteLine(var);
}
}
static void Main(string[] args)
{
TestMethod(1, 2, 3);
}
}
其他的方面与Java中的可变参数类似,看看Java中的写法:
for (int val : vals) {
System.out.println(val);
}
}
看到了吗?基本上差不多。
好了,到这里类和方法的第一部分就写完了,在第二部分中会讨论一些类的构造函数,属性等特性。