日期:2014-05-18  浏览次数:21159 次

C#传值或者传引用以及其他Tips

一、

struct以及以下的基本数据类型,赋值或者函数参数都传递的值(拷贝),除非使用ref关键字(函数参数)。

?

object的赋值或者函数参数都传递的引用,但是函数参数的时候,传递后的引用相当于一个常引用(不能=new xxx()之类了),如果使用ref关键字,?则这个引用可以自由修改。

也就是说,C#里object和struct以下的基本数据类型传递起来并没有太大不同,只是object变量本身的值就是指的引用(实际对象的地址)。

?

二、?

另外,怀念Delphi的with关键字,于是网上找到一个C#的实现:

首先定义这个extention method

?

public static void Use<T>(this T item, Action<T> work)
{
    work(item);
}

?其次可以这样使用了:

?

this.StatusProgressBar.Use(p =>
{
  p.IsIndeterminate = false;
  p.Visibility = Visibility.Visible;
  p.Minimum = 0;
  p.Maximum = 100;
  p.Value = percentage;
});

?

三、?

既然说到Action:

System.Func系列是带返回值类型的预定义delegator;而System.Action系列都是不带返回值类型的预定义delegator。

?

四、?

关于delegater(lambda表达式)?的capture,例如

?

void OneMethod()
{
    var local_var = SomeFactory.NewObj();
    if (_handler == null)
    {
         _handler = (s, e) => {CallSomeOtherMethod(local_var);};
    }
    UI.control1.OnSomeChange += _handler;
}

如果UI一直在“清空control1;new新的control1之后走OneMethod()挂载响应”这个循环,但是SomeFactory.NewObj()产生的local_var每次都不一样,那么每次挂载的哪个匿名lamba里面的local_var其实总是第一次的那个local_var,而非看似应该是的每次都在改变的local_var。

说白了就是,局部变量capture到lambda里面,都是相当于capture了一个值,如果一直用这个lambda,不管局部变量变成什么,这个lambda用的一直是创建这个lambda时该局部变量的取值。

?

?

?五、

关于extension method。用它可以在一定程度上满足对于类似于ruby的mixin module的需求。

例如,我的project中的每个类都需要加载资源字符串的方法。

当然使用StaticStringClass.StaticGetString(key)的方式,但是这样写起来——字符太多了。于是想让每个类中都有一个S(key)的方法可以用。继承不可行,C#单继承,这种功能上属于util的方法不适合当父类,何况也不应该让所有类有相同的父类。于是想通过mixin的方式来创造一个S(key)添加到类里面。

于是就用extension method这样解决:

?

    public interface ExtensionsInterfaceToHandleStringResource
    {
    }

    public static class ExtensionsInterfaceToHandleStringResourceImplementaion
    {
        public static String S(this ExtensionsInterfaceToHandleStringResource @this, string key)
        {
            // 读取资源字符串
            return ret;
        }
    }

? 这样只要声明自己继承 ExtensionsInterfaceToHandleStringResource的类,就都有了S(key)这个extension method。不过要注意,使用S(key)的时候必须加this前缀,即this.S(key),这个没辙了,extension method都这德行(必须是obj.XXX才能使用)。

?

?

六、

原来以为C#跟Java都是认为所有的方法都是virtual的(不根据指针/引用类型调用类定义所在内存的方法,而是根据实际对象的虚函数表寻找方法入口),今天一试,发现C#这方面还是C++之类“静态”语言的习惯,想要实现virtual方法,需要在基类的方法定义上声明virtual。嗯,虽然在关键字上更让人想起delphi,这蛮好的,继承类指明override让代码易读,调用和维护时不易出错。

?

觉得Java那样简化概念蛮好的,毕竟多态一般都是用接口(或父类)指针来调用实现类(或子类)的方法,而至少我都好像没怎么用到过“截断”式的调用。

?

回到C#,如果要用virtual式的(运行时绑定,动态绑定,对象所在内存的虚函数表,常见的Java式接口式多态),在父类方法使用virtual关键字,子类方法使用override关键字;如果要用“截断”式的(编译时绑定,静态绑定,即便是子类对象用父类指针调用也只能调用到父类的方法,方法入口在类的定义所在的内存而非对象所在内存的虚函数表),则父类使用普通方法就行,子类加一个new关键字,表达一个overwrite的意思,在delphi那里,相同情况下用的关键字是reintroduce。