this.Think();

(Define (Learn-SICP (Begin (Read Book) (Write Scheme) (Thinkabout It))))
随笔 - 13, 评论 - 139, 引用 - 1

导航

关于

关注于.Net, OO Design 和 Scheme

正在阅读:
SICP

Advanced .Net Remoting

已读完:

每月存档

最新留言

广告

Undocumented Keywords in C#

研究Int32&的时候,无意中发现C#里面还有4个Undocument Keyword, 分别是__makeref, __reftype, __refvalue 以及__arglist。 其中前三个keyword可以这样用:

        int i = 1;
        
        TypedReference tr 
= __makeref(i);
        Type t 
= __reftype(tr); //t = System.Int32
        
        
int i1 = __refvalue(tr, int); //i1 = 1
        int i2 = (int)TypedReference.ToObject(tr); //i2 = 1
        
        i
++//i = 2
        
        
int i3 = __refvalue(tr, int); //i3 = 2

(关于TypedReference类型MSDN是这样描述的:Describes objects that contain both a managed pointer to a location and a runtime representation of the type that may be stored at that location. 同时TypedReference有[CLSCompliant(false)]标记)

于是我们可以用下面这种方法来模拟ByRef的参数

public class MyClass
{
    
public static void Main()
    
{
        
int v = 99;
        TypedReference trParameters 
= __makeref(v);
        Foo(trParameters);

        Console.WriteLine(
"v = {0}", v); // v = 100 
    }

    

    
public static void Foo(TypedReference tr)
    
{
        
if(__reftype(tr) == typeof(int))
        
{
            __refvalue(tr, 
int)++;
        }

    }

}

比较不爽的就是我们必须在Foo方法体中判断TypedReference包含的类型。

注意如果把Foo写成public static void Foo(ref TypedReference tr),编译器会抱怨说:Method or delegate parameter cannot be of type 'ref System.TypedReference'。

至于__arglist则可以模拟params关键字,抄个例子:

public void Function(__arglist) 
{
    ArgIterator iterator 
= new ArgIterator(__arglist);
    
for (int count=iterator.GetRemainingCount(); count>0; count--)
    
{
        TypedReference typedRef 
= iterator.GetNextArg();
        Console.WriteLine(__refvalue(typedRef, 
int));
   }

}

调用它:Function(__arglist(2,3,4));  输出2,3,4

后注:这4个keyword毕竟是undocument的,微软也没有提供任何支持,所以不排除以后被delete掉。Mono下面的编译器似乎也不支持,不过我在vs2005 beta里面试验还是有效的。

 Updated: Flier Lu在他的blog对__arglist的使用作了更深入的探讨,推荐阅读。 :)

posted on 2005-08-18 18:49:00 by linkcd  评论(3) 阅读(3828)

System.Int32&是个啥?

话说有一天你要用反射来对Target类进行操作,调用Foo函数。
public class Target
...{
public void Foo(int x, int y, int z)
...{
}

public void Foo(int x, ref int y, out int z)
...{
}
}
可以看到Foo有2个重载,唯一的区别在于第二个Foo方法签名中有带ref的参数。
如果你直接写:

Target myTarget = new Target();
Type t = myTarget.GetType();
MethodInfo methodInfoWithRefParamters = t.GetMethod("Foo");
那么会在RunTime扔个Exception说: Ambiguous match found.
要想invoke第二个Foo的话,我们必须这样写:
MethodInfo methodInfoWithRefParamters ;
Type[] typeList = new Type[] {typeof(int), Type.GetType("System.Int32&"),Type.GetType("System.Int32&") };
methodInfoWithRefParamters = t.GetMethod("Foo", typeList );
好吧,现在运行是正常运行了,可是新的问题又来了:这个System.Int32&是个啥东东呢?
 
先在MSDN/.Net Reflector找找Int32&,没有。Google和查看微软放出的Framework Source Code,同样无果。
 
得,现成的资料找不到,那只有让我们自己通过做试验来玩玩了。
 
1。 先试试能不能搞一个Int32&的实例出来:

当然,就不用指望直接能在C#里面new一个Int32&了。(如果是unsafe的话,Int32*到还可以)
来试试亲爱的Activator吧:
Type refInt32Type = Type.GetType("System.Int32&");
object myRefInt = System.Activator.CreateInstance(refInt32Type, true);
哎呀,扔了一个“No parameterless constructor defined for this object.”异常出来。
 
2。好吧,那让我们来看看Int32&有啥样的constructor:
BindingFlags bf = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance
                              | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.NonPublic ;

Console.WriteLine(refInt32Type.GetConstructors(bf).Length);
结果却返回了0。

狠一点,把全部BindingFlags Enum放到bf上去呢,用GetMembers试试呢?

BindingFlags bf = // all values of BindingFlags
Console.WriteLine("count of member in type Int32&: {0}", Type.GetType("System.Int32&").GetMembers(bf).Length);
oops,输出长度还是0!
 3。Ok,Ok,不能创建实例就暂时不管它,那让我们用QuickWatch来看看这个Int32&类型有啥属性:

下面是我把Int32,Int32&, Int32*,外加Boolean&类型做对比的结果。(属性值相同的已经略过)
 
 
通过这张表我们可以观察到很有意思的结果:
Int32& 是ByRef的(最明显,呵呵),不是ValueType, 不可以序列化,没有Sealed,GUID都是0,BaseType 显示为《undefined value》
 
请特别注意的是Int32&, Int32*它们IsClass属性为True,说明是个Class,先记下来,在后面我会给大家看一个更有趣的结果。
 
既然,Int32&和Int32*都说自己有ElementType,那就来看看是什么Type吧:
Type.GetType("System.Int32&").GetElementType(); // Print “System.Int32”
Type.GetType("System.Int32*").GetElementType(); // Print “System.Int32” too.
4.回过头来看看生成的IL代码呢?
 

如图,只能看到对于ref/out 的参数,使用的是ldloca.s 指令,还是没看到我们的Int32&类型,
 
5.最后来看看最有趣的发现:
先试试在你的机器上run这3句代码:
Console.WriteLine(Type.GetType("System.Int32&").IsSubclassOf(typeof(object)));
Console.WriteLine(Type.GetType("System.Int32*").IsSubclassOf(typeof(object)));
Console.WriteLine(Type.GetType("System.Boolean*").IsSubclassOf(typeof(object)));
再看结果,呵呵,吃惊吧。以上3句都是输出False。难道说.Net里存在着不是从Object继承的Type吗?可它的IsClass属性是为True的哦~

会不会Int32&不是属于.Net框架的呢?可看看它的AssemblyQualifiedName是“System.Int32&, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”,也没看出什么问题啊?(汗)
 
个人初步猜测:
 
Int32&类型并不是一个真实存在的类型(原因是通过Reflector没能发现它的踪影),而是在CLR中动态构造的一个类型。MS使用它来“表示/标志”某个ValueType的地址。

2005/08/16 Update: 
思归的帖子,以及Joel Pobar的blog对这个问题作出了比较好的解释,有兴趣的读者可以看原文,偷懒的话就听我说一下吧
 
ByRef types 是受控指针,在CLR里面也是真实存在的类型。不过因为只有很少的IL指令能够作用与它,所以我们不能创建它的实例。同时,系统也限制我们只能在parameter的位置使用ByRef的类型。值得注意的是Int32&和Int32之间不存在什么关系哦...
 
 

posted on 2005-08-14 20:17:00 by linkcd  评论(12) 阅读(5561)

Powered by: Joycode.MVC引擎 0.5.2.0