C#基础教程(二)ref&out和const&readonly

用C#也开发了也好几个项目,再一章章写基本语法没啥效果,一些很基础的或已经熟用的就不写了,主要写一些常用但概念模糊、未熟记及重要的知识点。第一篇就写写数值传参,或许你会发现,跟C++很神似。

值传参时,实参可能会是一个值类型或者引用类型的变量。何谓值类型和引用类型?值类型就是真实数据存栈中,引用类型就是真实数据存堆中,地址存堆中,是不是很有变量和指针的味道。如果是值类型,栈中形参复制一个真实数据,占内存;相反,如果是引用类型,栈中则复制出一个引用地址,指向同一个真实数据

引用传参是,形参相当于是实参的别名,不重新开辟内存,形参变实参也跟着变。切勿忘记引用函数时候,写上修饰符ref或者out,两者同为引用传参,有啥异同呢?

差异:

1.参数初始化。ref不能用未初始化的实参传递,out则传递之前没有要求;

int x;
object.max(ref x)//会报错,一定要初始化

2.方法内形参使用。out在方法结束前必须显示给out形参赋值,ref没要求。ref可以把参数的数值传递进函数,但是out是要把实/形参数清空,就是说你无法把一个数值从out传递进去的,out进去后,参数的数值为空,所以你必须初始化一次。这个就是两个的区别,或者说就像有的网友说的,ref是有进有出,out是只出不进。

class Program
{
    static public int s=13;
    static void Main(string[] args)
    {
        min(out s);
    }
    static public void min(out int y)
    {
        int c = y;//y是(s)已经被清空为null,无法赋值,报错,必须先赋值后使用
        y = 12;
    }
}

相同:

1.当同时存在ref和out修饰符的同名函数时,无法编译,报错;

//无法重载,报错
public int max(out int z)
{
    z = 16;
    return z;
}
public void max(ref int x)
{
    x = 15;
}

2.重载,当有且仅有ref或out,可以重载函数。

出于篇幅考虑,我们再讲讲const和readonly的区别吧,否则太苍白无力了。之前在写C#代码时用的比较随意,只知道说这两个关键字一个是不变常量并且无法修改,一个是只读不能进行写操作,感觉都是只读,今天要深入挖掘一下。首先这两个修饰符牵扯到C#的静态常量和动态常量,不妨先看下概念:

静态常量(编译时常量)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值,const修饰即为静态常量,是不是很C++,宏定义中的预编译。

动态常量(运行时常量)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化,用readonly修饰。

readonly为运行时常量,程序运行时进行赋值,赋值完成后便无法更改,因此也有人称其为只读变量。

异:

1.const不能跟static同用,static全局存在,const编译时替代;readonly可以跟static一起用;

2.const定义的时候就要初始化,reandonly可以定义时初始化与否都可,可在构造函数中赋值初始化,其它地方不能赋值;

3.const可以修饰在编译的时候就需要有确定的值,只能用于数值类型(但不行结构体)和字符串,或者引用类型只能为null,因为一般对象引用都是在运行时分配内存决定的。而readonly在程序运行时才会去求值,它可以是任意类型,当然可以是object,数组,struct;

4.const可直接用于static方法,readonly如果用于静态方法必须用static修饰,跟普通变量用于static静态方法一样;

5.readonly还可以赋动态运行时值,比如DateTime.MinValue。

同:

1.都不能修饰属性;

2.一旦初始化后都不能改变。

是否很好奇,为什么string是引用类型,为什么能用const也能修饰?有些解释说string是个特殊的引用类型,其实我比较赞同这个解释:编译期常量仅能用于基本类型(内建的整数和浮点类型)、枚举或字符串。在编译后得到的IL代码中,只有这些常量可以直接被替换成为它们的字面值。

这里我就举两个有趣的小例子,上述的用法我就不具体写了,平时敲码的时候注意一下就行。

namespace constANDreadonly
{
    class Program
    {
        static readonly int A = B * 10;
        static readonly int B = 10; 
        static void Main(string[] args)
        {
            Console.WriteLine("A is {0},B is {1} ", A, B);
        }
    }
}

我想很多人会认为是A is 100,B is 10!其实,正确的输出结果是A is 0,B is 10。static readonly是动态常量,变量的值在编译期间不予以解析,所以开始都是默认值,像A与B都是int类型,故都是0。而在程序执行到A=B*10;变成A=0*10=0,程序接着执行到B=10这句时候,才会真正的B的初值10赋给B。

namespace constANDreadonly
{
    class Program
    {
        const int A = B * 10;
        const int B = 10;
        public static void Main(string[] args)
        {
            Console.WriteLine("A is {0},B is {1} ", A, B);
        }
    }
}

答案还会像上面一样吗?当然不,这次输出结果是A is 100,B is 10!上面说了,const是静态常量,所以在编译的时候就将A与B的值确定下来了(即B变量时10,而A=B*10=10*10=100),那么Main函数中的输出当然是A is 100,B is 10。

总结

静态常量和动态常量的本质区别,造成不用的用法规则,我们用表格总结一些这些区别:

静态常量动态常量
内存消耗因需保存常量,需要消耗
初始化很少的简单类型,不能New,必须在定义时初始化任意类型,可以在构造函数中初始化
何时发挥作用在编译的时候进行替换相当于类的数据成员

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值