MSDN 官方的解释

readonly 关键字是可以在字段上使用的修饰符。当字段声明包括 readonly 修饰符时,该声明引入的字段赋值只能作为声明的一部分出现,或者出现在同一类的构造函数中.
 
很多初学者看完书就会认为,readonly修饰的变量在以后是不能修改的,在以后的开发中从不对ReadOnly的变量进行修改操作,形成思维定势,这个观念是错误的。

首先要明确一点:更改!=重新分配(赋值)

对于简单类型(如int),更改是等于重新赋值,因为默认操作符只有=, 但于对于复杂类型,就不一定了。

例如:

对于class类型,修改其字段属性值。

对于集合类型,增加,移除,清空内容。

 

我们经常在微软的代码中发现readonly的如下类似的用法:

1     public interface IA { }
2     public class A1 : IA { }
3     public class A2 : IA { }
4 
5     public static class AContainer
6     {
7         private static readonly Dictionary<string, IA> Items = new Dictionary<string, IA>();
8         public static Dictionary<string, IA> As{ get { return Items; } }
9     }

然后在外部可以修改AContainer

 

 1 class Program
 2     {
 3         static void Main()
 4         {
 5             Console.WriteLine(AContainer.As.Count);
 6             AContainer.As.Add("A1"new A1());
 7             Console.WriteLine(AContainer.As.Count);
 8             AContainer.As.Add("A2"new A2());
 9             Console.WriteLine(AContainer.As.Count);
10             // we can remove all the item of the collection
11             AContainer.As.Clear();
12             Console.WriteLine(AContainer.As.Count);
13             Console.ReadKey();
14         }
15 
16     }

 

输出:

 

 

结论:

可以在外部(非声明和构造函数)对readonly的变量进行修改内容操作。

 

微软示例和开源代码中用的一种开发扩展的方式就是使用静态的ReadOnly集合容器类,在其中包括默认的实现,然后允许用户在开发中进行添加或替换。

如MVC3中的 ModelBinderProviders,ViewEngines都是类似的实现。

 

当然我们还可以通过非常规手段(反射,操作内存)来改变readonly的值。

例如反射

 

 1 using System;
 2 using System.Reflection;
 3 
 4 namespace SOVT
 5 {
 6     public class Foo
 7     {
 8         private readonly int bar;
 9         public Foo(int num) { bar = num; }
10         public int GetBar() { return bar; }
11     }
12 
13     class Program
14     {
15         static void Main()
16         {
17             Foo foo = new Foo(123);
18             Console.WriteLine(foo.GetBar());
19             FieldInfo fieldInfo = typeof(Foo).GetField("bar", BindingFlags.Instance | BindingFlags.NonPublic);
20             if (fieldInfo != null)
21                 fieldInfo.SetValue(foo, 567);
22             Console.WriteLine(foo.GetBar());
23             Console.ReadKey();
24         }
25     }
26 }

 

输出

 

 

posted @ 2011-12-19 12:04 sujiantao 阅读(1083) 评论(6) 编辑

这是个老话题了,不过还是有一些初学者不是太明白,这个也是在面试时容易问的问题。

 

试验1:Ref对于改变引用对象的属性时的影响

using System;

namespace SOVT
{
    class Program
    {
        static void Main()
        {
            MyClass myClass=new MyClass();
            Console.WriteLine(myClass.Field);
            ChangeMyClass(myClass);
            Console.WriteLine(myClass.Field);
            ChangeMyClass(ref myClass);
            Console.WriteLine(myClass.Field);
            Console.ReadKey();
        }
        public class MyClass
        {
            public int Field;
        }

        public static void ChangeMyClass(MyClass myClass)
        {
            myClass.Field = 1;
        }

        public static void ChangeMyClass(ref MyClass myClass)
        {
            myClass.Field = 2;
        }
    }
}

其运行结果为:

 

结论:对于引用对象,无论是否有Ref关键字修饰,都能改变其调用方法外部的引用的对象的字段值,用不用Ref无影响。

 

试验2:Ref对于在方法内部改变其指向对象时影响

 

using System;

namespace SOVT
{
    class Program
    {
        static void Main()
        {
            MyClass myClass=new MyClass();
            Console.WriteLine(myClass.Field);
            ChangeMyClass(myClass);
            Console.WriteLine(myClass.Field);
            ChangeMyClass(ref myClass);
            Console.WriteLine(myClass.Field);
            Console.ReadKey();
        }
        public class MyClass
        {
            public int Field;
        }

        public static void ChangeMyClass(MyClass myClass)
        {
            myClass = new MyClass {Field = 1};
        }

        public static void ChangeMyClass(ref MyClass myClass)
        {
            myClass = new MyClass { Field = 2 };
        }
    }
}

 

运行结果:

 

结论:对于再分配引用对象的引用的操作,没有Ref修饰时是不能在外部生效的,只有有Ref修饰的引用参数才能使再分配操作应用于外部。

 

小结:

  1. 当没有Ref修饰引用类型的参数时,在方法中只能改变此引用参数的属性内容,而不能改变整个引用参数对象本身。
  2. 当有Ref修饰引用类型的参数时,则可以在方法中改变此引用参数的指向位置,而不仅是其属性内容。
  3. 当一个引用类型传给一个方法时,是传递了这个引用类型对象的引用的副本(不是对象本身),所以对于在调用方法外部的引用和方法中的引用来说,这两个引用都指向堆上的同一个对象。所以在修改此对象的属性值时,修改同时会应用于内部和外部的两个引用上。但重新分配其引用位置时,则只是修改副本引用的引用位置,原引用(方法外部)的位置不变,原引用还是指向原来的对象。
  4. 而如果加上Ref关键字,这时传入的参数则为些引用对象的原始引用,而不是引用的副本,这样的话,你就不但可以修改原始引用对象的内容,还可以再分配此引用的引用位置(用New 来重新初始化)。
  5. 如果你只想在方法中改变引用参数的内容,没有必要使用Ref来修饰引用参数。
  6. 如果你希望在方法中改变引用对象参数的引用(调用方法外的),如重新初始化对象,则需要使用Ref关键字来修饰参数。
 
posted @ 2011-12-16 09:13 sujiantao 阅读(1235) 评论(3) 编辑

感觉不错,功能挺符合习惯,赞一个

posted @ 2010-07-14 00:25 sujiantao 阅读(35) 评论(0) 编辑

如下情况,下面代码只是测试用,不讨论其它:

 

Code

 

我们想得到的结果可能是:

1  1   pro1
2       pro2
3  2   pro3
4       pro4
5       pro5

不过得到的结果是

1  1   pro1
2  0    pro2
3  2   pro3
4  0    pro4
5  0    pro5

因为type字段是int型,所以最后select的type类型也默认成了int型,对int型赋值'',得出的为0。

所以如果是对int操作时,要加上类型转换,防止不想要的0的出现:

 

Code

 

顺便说一句,case when then在某些情况下有很大的性能问题。

 

posted @ 2009-02-24 15:12 sujiantao 阅读(441) 评论(0) 编辑

前几天美工给了个页面,我套上后,测试没通过,在firefox中有问题。当时还找了很长时间的bug后来发现是label的问题。

测试页面如下:

一个页面上放了4个按扭,四个按扭在一个label中,点击每个按扭分别会触发一个js,弹出一个alert

Code

 

上面的代码在ie中是正确的,点击按扭1,alert 1...点击按扭4,alert 4。

但是在firefox中:

点击按扭1 alert 1

点击按扭2 aerlt 2,alert 1

点击按扭3 alert 3,alert 1

点击按扭4 alert 4 ,alert 1

可以看出,在firefox中,在一个label中如果有多个控件,点击非第一个控件时总会同是触发第一个控件的js

解决方法就是把label去掉。

posted @ 2008-10-07 11:13 sujiantao 阅读(134) 评论(1) 编辑