c#中的反射

2016-06-23  本文已影响247人  修行猿

都说c#和java差不多,会了java,c#基本都会了。我对java还是很熟的,对oc也比较熟,但是c#感觉还是过一遍比较好。最近看到了c#的反射,这里呢就对反射在各个语言中的体现横向比较一下。

反射,让程序员能够根据一个字符串去:

  1. 实例化对象
  2. 调用想要的方法
  3. 遍历类中或者对象中的属性或者属性值。

意思就是能够动态调用。这样会有非常非常大的好处,以及便捷性。反射这一套东西在开发业务中不是必须的,但是如果你想让你的代码有:

  1. 更好的组织性
  2. 更松的耦合性
  3. 更强的复用性

反射必须掌握。而且语言之间都是相通的。如果你是一个想偷懒的程序员,一定要掌握反射。

1.如何从一个字符串实例化一个类

c#

Type type = Type.GetType("Person",true);
Person p =  (Person)System.Activator.CreateInstance(type);

java

 Object o = Class.forName("com.company.Main").newInstance();

oc

Class class = NSClassFromString(@"YXTestObject");
YXTestObject *obj= [[class alloc] init];
或者
Class class = objc_getClass("YXTestObject");
 YXTestObject *obj= [[class alloc] init];

js

//js就比较灵活一点

//第一种方式:这种方式有一定的不安全性,特别是当类名从用户那边获取的时候
var myStr = "Product"
var p = eval("new " + myStr + "()");

//第二种:将类定义在一个域中
var mynamespace = {};
mynamespace.Person = function Person() {..}
var p = new mynamespace["Person"]();

//如果直接定义在全局域可以这样:
var p = new window["Person"]();

2.如何通过一个字符串调用某个对象的某个方法

c#

Person p = new Person();

//有参公有
var methodInfo =   p.GetType().GetMethod("set_Name");
methodInfo.Invoke(p, new string[] {"aaa"});

//无参公有
var methodInfo2 = p.GetType().GetMethod("get_Name");
var  name =  methodInfo2.Invoke(p, null);

//静态函数公有
p.GetType().GetMethod("getaaa").Invoke(null,null)

//非静态私有
var methodInfo2 = p.GetType().GetMethod("getaaa",BindingFlags.NonPublic | BindingFlags.Instance);
var  result =  methodInfo2.Invoke(p, null);

//静态私有
 var methodInfo2 = p.GetType().GetMethod("getaaa",BindingFlags.NonPublic | BindingFlags.Static);
 var  name =  methodInfo2.Invoke(p, null);

java

//公有
Person p =(Person) Class.forName("com.company.Person").newInstance();
String number =   (String)Person.class.getMethod("getNumber", null).invoke(p,null);

//私有
Person p =(Person) Class.forName("com.company.Person").newInstance();
Method method =   Person.class.getDeclaredMethod("getNumber", null);
method.setAccessible(true);
String number=(String)method .invoke(p,null);

//Java中反射静态方法和调用普通的公有私有方法无太大区别
//只是忽略了invoke中的对象参数
//getDeclaredMethod方法可以获取当前类的所有方法
//但是不包含继承的方法,如果想获取继承的方法,可以通过getSuperclass向上寻找。
//getMethod方法只会获取public类型的

oc

//方式1:这种方式,最多支持2个参数
NSString *number = [obj performSelector:NSSelectorFromString(@"getNumber") withObject:nil withObject:nil];
//方式2
//支持多参
//导入#import <objc/message.h>
//Build Setting--> Apple LLVM 6.0 - Preprocessing--> Enable Strict Checking of objc_msgSend Calls  改为 NO
//否则会报Too many arguments to function call ,expected 0,have3
NSString *number2 = objc_msgSend(obj,NSSelectorFromString(@"getNumber"),nil);

//类方法
NSString *number2 = objc_msgSend(class,NSSelectorFromString(@"getNumber"),nil);

//NSSelectorFromString(@"getNumber")等价于sel_registerName("getNumber")

//上面的几种方式不管在h文件中有没有声明都能被调用到

js

//js就非常简单了
var p = new Person();
var number = p["getNumber"]();

3.通过属性名称字符串获取某个对象的字段值

c#

//公有
p.GetType().GetField("number").SetValue(p,"123456");

//私有
p.GetType().GetField("number",BindingFlags.NonPublic|BindingFlags.Instance).SetValue(p,"123456");

//私有静态
p.GetType().GetField("number",BindingFlags.NonPublic|BindingFlags.Static).SetValue(p,"123456");

Java

  Field field  =  Person.class.getDeclaredField("name");
 field.setAccessible(true);  //是否设置强制访问私有
 field.set(p, "aaaa");          //设置字段值
 String name = (String)field.get(p);  //获取字段值

oc

//可以通过kvc的方式,成员变量不管被声明在m文件还是h文件
//不管是私有还是公有都能通过这种方式被访问到
 YXTestObject *obj= [[class alloc] init];
[obj setValue:@"男" forKey:@"sex"];
NSLog(@"%@",[obj valueForKey:@"sex"]);

js

js依然是最简单的
var p =  new Person();
p["number"]="123456";
var number= p["number"];

4.遍历某个对象的字段以及字段值

c#

public class Test {        
public static void reflect(Object o){  
  
    PropertyInfo[] propertys = o.GetType().GetProperties();  
    foreach (PropertyInfo pinfo in propertys)  
    {  
        Console.WriteLine(pinfo.Name+","+pinfo.GetValue(o,null));  
    }  
}  
static void Main(string[] args){  
    Person p = new Person();  
    reflect(p);  
}  

}

java

public class Test {        
public static void reflect(Object o) throws Exception{  
    Class cls = o.getClass();  
    Field[] fields = cls.getDeclaredFields();  
    for(int i=0; i<fields.length; i++){  
        Field f = fields[i];  
        f.setAccessible(true);  
        System.out.println("属性名:" + f.getName() + " 属性值:" + f.get(o));  
    }   
}  
public static void main(String[] args) throws Exception{  
    Person p = new Person();  
    reflect(p);  
}  

}

oc

+(NSString*)reflectWithObj:(id)obj
{
    NSString *name = nil;
    NSString *value =nil;
    uint32_t ivarCount;

    Ivar *ivars = class_copyIvarList([obj class], &ivarCount);
    
    if(ivars)
    {
        for(uint32_t i=0; i<ivarCount; i++)
        {
            Ivar ivar = ivars[i];
            
            name = [NSString stringWithUTF8String:ivar_getName(ivar)];
            value=[obj valueForKey:name];
            NSLog(@"name=%@,value=%@",name,value==nil?@"nil":value);
        }
        
        free(ivars);
    }

    return name;
}   

Js

function allPrpos ( obj ) {   
    var props = "" ;   
    for ( var p in obj ){   
        if ( typeof ( obj [ p ]) == " function " ){   
        obj [p]() ;   
        } else {   
            props += p + " = " + obj [ p ] + " \t " ;   
        }   
    }   
    console.log( props ) ;   
}

c#中类似的还有

  1. 获取某个类的ConstructorInfo(构造器信息): 通过GetConstructors或者GetConstructor方法。

  2. 获取某个类的EventInfo(类中的事件信息):通过GetEvents或者GetEvent方法。

  3. 获取某个类的PropertyInfo(类中的属性信息):通过GetProperties或者GetProperty方法。

  4. 如何加载程序集

    Assembly.Load("相对路径");
    Assembly.LoadFrom("完整路径");

  5. 如何取得程序集中所有的Type

    Type[] types = Assembly.Load("aaa").GetTypes();

Java中也有类似的反射方法,以获取构造器,等类中的一些定义属性。
OC 是动态语言就反射来说效率会高于静态语言java和c#。
Javascript总的来说是最灵活的。

反射还有很多,这里就不一一赘述,总的来说使用率比较高的就是我介绍的这几个。当然实际肯定还有各种各样的需求,慢慢查api吧,总结是总结不完的,这里只是把常用的列出来。

PS:写代码这么久了,还没有像样的博客,之前建了三次,忘了三次都被阿里云最后回收了。这次换社区试试,希望能坚持下去。会把以前的笔记,经验,还有工作中的一些问题,总结一下,慢慢的放到博客上。

上一篇下一篇

猜你喜欢

热点阅读