淺顯易懂的C#之反射教程。本站提示廣大學習愛好者:(淺顯易懂的C#之反射教程)文章只能為提供參考,不一定能成為您想要的結果。以下是淺顯易懂的C#之反射教程正文
媒介
之所以要寫這篇關於C#反射的漫筆,原由有兩個:
第一個是本身開辟的網站須要用到
其次就是沒看到這方面比擬好的文章。
所以下定決計本身寫一篇,空話不多說開端進入正題。
後期預備
在VS2012中新建一個掌握台運用法式(我的定名是ReflectionStudy),這個項目是基於.net 4.0。接著我們翻開Program.cs文件,依照以下在Program中寫一個我們本身的類:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
public void Show()
{
}
}
窺視外部
常言道知彼親信百戰百勝,所以我們第一步也是症結的一步就是要窺視RefClass類的構造(這裡我們假定對RefClass其實不懂得)。
起首我們先要縱覽全局能力持續深刻,所以我們先在Main中寫入以下代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
MemberInfo[] minfos = t.GetMembers();
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name);
}
Console.ReadKey();
}
在這裡我們獲得這個類的類型,然後獲得了個中的公共成員(能夠許多人都邑以為GetMembers是獲得全體,但其實只是獲得地下的一切成員。)然後我們經由過程foreach將一切的成員的稱號輪回輸入。
然後我們可以檢查掌握台的輸入:
在這裡我們可以看到個中不只僅輸入了我們所寫類中的成員,同時還輸入了父類的成員(假如不睬解的這裡幫你們彌補下基本,Object是一切類的基類。),仔細的讀者必定會發明這裡的輸入並沒有包括private和protected拜訪權限的成員。這就應了下面的那句話:GetMembers默許前往地下的成員。
僅僅只能看到這些地下的成員對我們來講意義其實不年夜,所以我們須要檢查到那些非私有的成員。
上面我們將下面的代碼改成以下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public );
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name);
}
Console.ReadKey();
}
從中我們看到我們應用了GetMembers的重載版本,而且傳入了列舉類型,分離是“包括非地下”、“包括實例成員”和“包括地下”。然後我們便可以獲得到一切成員了。
終究我們將會得出上面這些成員:
到這裡你能夠會以為我們曾經檢索停止了,然則你有無發明屬性許多,並且還包括了年夜量的父類中的屬性,假定我們只存眷該類中的成員,其實不存眷父類中的成員該若何做呢?
其實我們只須要加上一個列舉類型(BindingFlags.DeclaredOnly):
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly );
然後我們再檢查成果:
此時就只包括該類中的成員了。
上面我們在RefClass類中添加兩個靜態辦法,以下所示:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
private static void Show2()
{
}
public static void Show3()
{
}
public void Show()
{
}
}
然後我們持續檢查,可以發明終究的成果並沒有輸入這些靜態成員。這個時刻我們只須要在GetMembers中加上一個列舉:BindingFlags.Static便可。
這裡我們僅僅輸入了一切的成員,然則卻沒有辨別出是辦法照樣屬性所以我們在Main中添加一個辦法:
static void Main(string[] args)
{
Type t = typeof(RefClass);
Func<MemberTypes, String> getType = (x) =>
{
switch (x)
{
case MemberTypes.Field:
{
return "字段";
}
case MemberTypes.Method:
{
return "辦法";
}
case MemberTypes.Property:
{
return "屬性";
}
default:
{
return "未知";
}
}
};
MemberInfo[] minfos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static );
foreach (MemberInfo minfo in minfos)
{
Console.WriteLine(minfo.Name + ";類型:" + getType(minfo.MemberType));
}
Console.ReadKey();
}
這裡我用了一個部分辦法來依據類型輸入對應的文本,由於篇幅的緣由我就只斷定了幾個根本的類型。
終究輸入的成果以下:
到此為止我們曾經可以或許窺視全部構造。
深刻窺視字段
經由過程下面的內容我們僅僅縱覽了全局,上面我們將要持續深刻,起首我們先拿字段下手。
這裡我們不在應用GetMembers而須要應用GetFields(固然跟GetMembers一樣假如不傳入指定的列舉只前往地下的字段),代碼以下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
Console.WriteLine("字段稱號:{0} 字段類型:{1} ", finfo.Name, finfo.FieldType.ToString());
}
Console.ReadKey();
}
終究的輸入成果以下所示:
一向到這裡年夜家都邑以為我們僅僅只是剖析,感到沒有甚麼本質的器械,上面就來點本質的器械,你可以看到_test3、_test1和Test2是公有和掩護類型,
是弗成以獲得到它們的值的,然則我們經由過程反射卻可以,詳細的代碼以下所示:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
Console.WriteLine("字段稱號:{0} 字段類型:{1} rc中的值為:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
}
Console.ReadKey();
}
可以看到我實例化了這個類,而且設置了Test3為3,上面我經由過程finfo.GetValue輸入了這個值,成果以下圖:
如今是否是感到有點酷了?這還沒完呢,我們光獲得不算甚麼,上面我們還要修正它的值:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
FieldInfo[] finfos = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo finfo in finfos)
{
finfo.SetValue(rc, 100);
Console.WriteLine("字段稱號:{0} 字段類型:{1} rc中的值為:{2}", finfo.Name, finfo.FieldType.ToString(), finfo.GetValue(rc));
}
Console.ReadKey();
}
這裡我只是在foreach中增長了一條語句finfo.SetValue(rc,100),上面我們持續看終究輸入的成果:
是否是如今感到可認為所欲為了?然則還沒有完。
深刻窺視屬性
由於屬性存在get和set,而且二者都是辦法,所以比擬辣手。我們須要經由過程屬性對象獲得get和set辦法,在經由過程挪用他們才到達修正這個屬性的值。
好比上面的代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
PropertyInfo[] finfos = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (PropertyInfo finfo in finfos)
{
MethodInfo getinfo = finfo.GetGetMethod(true);
Console.WriteLine("get辦法的稱號{0} 前往值類型:{1} 參數數目:{2} MSIL代碼長度:{3} 部分變量數目:{4}", getinfo.Name, getinfo.ReturnType.ToString(),
getinfo.GetParameters().Count(),
getinfo.GetMethodBody().GetILAsByteArray().Length,
getinfo.GetMethodBody().LocalVariables.Count);
MethodInfo setinfo = finfo.GetSetMethod(true);
Console.WriteLine("get辦法的稱號{0} 前往值類型:{1} 參數數目:{2} MSIL代碼長度:{3} 部分變量數目:{4}", setinfo.Name, setinfo.ReturnType.ToString(),
setinfo.GetParameters().Count(),
setinfo.GetMethodBody().GetILAsByteArray().Length,
setinfo.GetMethodBody().LocalVariables.Count);
setinfo.Invoke(rc, new object[] { 123 });
object obj = getinfo.Invoke(rc, null);
Console.WriteLine("辦法名:{0} 外部值:{1}", finfo.Name, obj);
}
Console.ReadKey();
}
這裡我們輪回每一個屬性,經由過程GetGetMethod獲得get辦法(挪用該辦法時假如傳入true則沒法獲得非地下的get辦法set也是一樣),接著我們輸入了該辦法的前往類型和參數數目和MSIL代碼長度和部分變量的數目,
固然你假如有興致可以持續剖析輸出參數和部分變量等,這裡因為篇幅的原因就不克不及引見太多了。最初我們挪用了set辦法將值轉變,然後再經由過程挪用get辦法獲得這個屬性的值。
終究的成果以下所示:
深刻窺視辦法
起首我們須要將RefClass修正成以下所示:
public class RefClass
{
private int _test3;
private int _test1 { get; set; }
protected int Test2 { get; set; }
public int Test3 { get; set; }
private static void Show2()
{
}
public static string Show3(string s)
{
int b;
int c;
return s;
}
public string Show(string s)
{
string a;
return s;
}
}
重要是在辦法中增長部分變量而且加上前往值,防止最初輸入的時刻沒有值。其實這裡的辦法跟屬性部門相似,然則為了可以或許完全的描寫一切,所以筆者仍然會講授一遍。
上面我們直接上代碼:
static void Main(string[] args)
{
Type t = typeof(RefClass);
RefClass rc = new RefClass();
rc.Test3 = 3;
MethodInfo[] finfos = t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static );
foreach (MethodInfo finfo in finfos)
{
if (finfo.GetParameters().Count() > 0 && finfo.GetParameters()[0].ParameterType == typeof(string) )
{
object obj = finfo.Invoke(rc, new[] { "123" });
MethodBody mbody = finfo.GetMethodBody();
Console.WriteLine("具有參數的辦法名:{0} 前往值類型:{1} 參數1類型:{2} 參數1稱號:{3} 辦法挪用後前往的值:{4}",
finfo.Name,
finfo.ReturnType.ToString(),
finfo.GetParameters()[0].ParameterType.ToString(),
finfo.GetParameters()[0].Name,
obj.ToString());
}
else
{
MethodBody mbody = finfo.GetMethodBody();
Console.WriteLine("沒有參數的辦法名:{0} 前往值類型:{1}",
finfo.Name,
finfo.ReturnType.ToString());
}
}
Console.ReadKey();
}
在這裡我停止了一些簡略的斷定好比斷定輸出參數的數目和類型,假如不停止這些斷定就會招致法式沒法持續履行,詳細為何可以看下的輸入成果,你就可以明確筆者為何要這麼做了。
上面就是詳細的成果:
讀者必定發明了這個中還有get和set,你能夠會以為它們不是屬性嗎?怎樣跑到辦法這裡來了,其實下面我曾經說了。這些其實也是辦法。這也是為何下面我須要去斷定輸出參數的數目和類型的原因。