.net程序员的盲点(一):参数修饰符ref,out ,params的区别
C#中有三个关键字-ref,out ,params,虽然本人不喜欢这三个关键字,因为它们疑似破坏面向对象特性。但是既然m$把融入在c#体系中,那么我们就来认识一下参数修饰符ref,out ,params吧,还有它们的区别。
NO.1 params
一个可以让方法(函数)的拥有可变参数的关键字。
原则:在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。
示例(拷贝到vs2005中即可用,下面不再说明)
public partial class Form1 : Form
...{
public static void UseParams(params int[] list)
...{
string temp = "";
for (int i = 0; i < list.Length; i++)
temp = temp +" " +list[i].ToString();
MessageBox.Show(temp);
}
public static void UseParams2(params object[] list)
...{
string temp = "";
for (int i = 0; i < list.Length; i++)
temp = temp + " " + list[i].ToString();
MessageBox.Show(temp);
}
public Form1()
...{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
...{
UseParams(1, 2, 3); //看参数是3个
UseParams(1, 2); //看参数是2个,可变吧
UseParams2(1, 'a', "test");
int[] myarray = new int[3] ...{ 10, 11, 12 };
UseParams(myarray); //看也可以是容器类,可变吧:)
}
}
NO.2 out
这是一个引用传递L。
原则一:当一个方法(函数)在使用out作为参数时,在方法中(函数)对out参数所做的任何更改都将反映在该变量中。
原则二:当希望方法返回多个值时,声明 out 方法非常有用。使用 out 参数的方法仍然可以返回一个值。一个方法可以有一个以上的 out 参数。
原则三:若要使用 out 参数,必须将参数作为 out 参数显式传递到方法。out 参数的值不会传递到 out 参数。
原则四:不必初始化作为 out 参数传递的变量,因为out 参数在进入方法(函数)时后清空自己,使自己变成一个干净的参数,也因为这个原因必须在方法返回之前为 out 参数赋值(只有地址没有值的参数是不能被.net接受的)。
原则五:属性不是变量,不能作为 out 参数传递。
原则六:如果两个方法的声明仅在 out 的使用方面不同,则会发生重载。不过,无法定义仅在 ref 和 out 方面不同的重载。例如,以下重载声明是有效的:
class MyClass
{
public void MyMethod(int i) {i = 10; }
public void MyMethod(out int i) {i = 10; }
}
而以下重载声明是无效的:
class MyClass
{
public void MyMethod(out int i) {i = 10; }
public void MyMethod(ref int i) {i = 10; }
}
有关传递数组的信息,请参见使用 ref 和 out 传递数组。
示例附后
NO.2 ref
ref仅仅是一个地址!!!
原则一:当一个方法(函数)在使用ref作为参数时,在方法中(函数)对ref参数所做的任何更改都将反映在该变量中。
原则二:调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。
原则三:若要使用 ref 参数,必须将参数作为 ref 参数显式传递到方法。ref 参数的值可以被传递到 ref 参数。
原则四:ref参数传递的变量必须初始化,因为ref参数在进入方法(函数)时后还是它自己,它这个地址指向的还是原来的值,也因为这个原因ref参数也可以在使用它的方法内部不操作。
原则六:如果两种方法的声明仅在它们对 ref 的使用方面不同,则将出现重载。但是,无法定义仅在 ref 和 out 方面不同的重载。例如,以下重载声明是有效的:
class MyClass
{
public void MyMethod(int i) {i = 10; }
public void MyMethod(ref int i) {i = 10; }
}
但以下重载声明是无效的:
class MyClass
{
public void MyMethod(out int i) {i = 10; }
public void MyMethod(ref int i) {i = 10; }
}
有关传递数组的信息,请参见使用 ref 和 out 传递数组。
示例
public static string TestOut(out string i)
...{
i = "out b";
return "return value";
}
public static void TestRef(ref string i)
...{
//改变参数
i = "ref b";
}
public static void TestNoRef(string refi)
...{
// 不用改变任何东西,这个太明显了
refi = "on c";
}
public Form1()
...{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
...{
string outi; //不需要初始化
MessageBox.Show(TestOut(out outi)); //返回值
//输出"return value";
MessageBox.Show(outi); //调用后的out参数
//输出"out b";
string refi = "a"; // 必须初始化
TestRef(ref refi); // 调用参数
MessageBox.Show(refi);
//输出"ref b";
TestNoRef(refi); //不使用ref
MessageBox.Show(refi);
//输出"ref b";
}
.net程序员的盲点(二):两个“属性”引起的歧异
两个“属性”引起的歧异-property和attribute的区别
这虽然没有“一个馒头引发的血案”那么严重,但是也足以成为.net程序员的技术盲点之二。
对于property和attribute这两个名词都叫“属性”的问题,来源于国内it书籍翻译界的疏忽。
其实它们来源于两个不同的领域,attribute属于OOA/OOD的概念,而property属于编程语言中的概念。下面我们来说明它们的异同。
Attribute
Attributes是Microsoft .NET Framework文件的元数据,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。
Property
属性是面向对象编程的基本概念,提供了对私有字段的访问封装,在C#中以get和set访问器方法实现对可读可写属性的操作,提供了安全和灵活的数据访问封装。关于属性的概念,不是本文的重点,而且相信大部分的技术人员应该对属性有清晰的概念。以下是简单的属性
区别
可以说两者没有可比性,只不过我们国家的语言特点才引起的歧异,其实只要记住Attributes是派生于System,Attributes类之下,它的主要作用是描述,比如在某一个自定义控件中的“属性”就是指Attribute,
如
[DllImport("User32.dll")]
Attribute也有很多系统的“默认”属性,见下表
预定义的属性 有效目标 说明
AttributeUsage Class 指定另一个属性类的有效使用方式
CLSCompliant 全部 指出程序元素是否与CLS兼容
Conditional Method 指出如果没有定义相关联的字符串,编译器就可以忽略对这个方法的任何调用
DllImport Method 指定包含外部方法的实现的DLL位置
STAThread Method(Main) 指出程序的默认线程模型为STA
MTAThread Method(Main) 指出程序的默认模型为多线程(MTA)
Obsolete 除了Assembly、Module、Parameter和Return 将一个元素标示为不可用,通知用户此元素将被从未来的产品
ParamArray Parameter 允许单个参数被隐式地当作params(数组)参数对待
Serializable Class、Struct、enum、delegate 指定这种类型的所有公共和私有字段可以被串行化
NonSerialized Field 应用于被标示为可串行化的类的字段,指出这些字段将不可被串行化
StructLayout Class、struct 指定类或结构的数据布局的性质,比如Auto、Explicit或sequential
ThreadStatic Field(静态) 实现线程局部存储(TLS)。不能跨多个线程共享给定的静态字段,每个线程拥有这个静态字段的副本
而Property是指编程过程中的字段,也即类的成员。
如:
private int hour; //定义私有变量表示"小时",外部是访问不到的.}
public int Hour// 定义Hour程序接口
{
set { hour=value; }
get { return hour; }
}