扩展方法到底做了什么
扩展方法是.net3.5以后出现的一个比较优雅的写法,昨天讨论中有同学提出这也是通过反射实现的,不过我并不这么认为。
扩展方法的定义必须是静态类的静态方法,对一个静态类的静态方法进行反射明显不明智。而且linq to object中大量使用扩展方法,如果是用发射实现方法调用,明显过于笨拙。
那么事实到底如何
先定义一个扩展方法和一个同样功能的非扩展方法
{
/// <summary>
/// 裁减字符串(扩展方法写法)
/// </summary>
/// <param name="source"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string CutStr(this string source, int length, string lastString)
{
if (string.IsNullOrEmpty(source))
return "";
if (source.Length > length)
{
string result = string.Empty;
source.ToCharArray().Take(length).ToList().ForEach(p => { result += p.ToString(); });
return result + lastString;
}
else
return source;
}
/// <summary>
/// 裁减字符串(非扩展方法写法)
/// </summary>
/// <param name="source"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string CutStrNew(string source, int length, string lastString)
{
if (string.IsNullOrEmpty(source))
return "";
if (source.Length > length)
{
string result = string.Empty;
source.ToCharArray().Take(length).ToList().ForEach(p => { result += p.ToString(); });
return result + lastString;
}
else
return source;
}
}
然后分别进行扩展方法调用,静态方法调用,反射调用三种调用
{
static void Main(string[] args)
{
string str = "hello world!";
Stopwatch watch = new Stopwatch();
Console.WriteLine(str.CutStr(5, "..."));
Console.WriteLine(StringHelp.StringHelp.CutStrNew(str, 5, "..."));
Console.WriteLine(typeof(StringHelp.StringHelp).GetMethod("CutStr").Invoke(null, new object[] { str, 5, "..." }));
//效率比较
watch.Start();
for (int i = 0; i < 100000; i++)
{
str.CutStr(5, "...");
}
watch.Stop();
Console.WriteLine("1:"+watch.ElapsedMilliseconds);
watch.Restart();
for (int i = 0; i < 100000; i++)
{
StringHelp.StringHelp.CutStrNew(str, 5, "...");
}
watch.Stop();
Console.WriteLine("2:" + watch.ElapsedMilliseconds);
watch.Restart();
for (int i = 0; i < 100000; i++)
{
typeof(StringHelp.StringHelp).GetMethod("CutStr").Invoke(null, new object[] { str, 5, "..." });
}
watch.Stop();
Console.WriteLine("3:" + watch.ElapsedMilliseconds);
Console.Read();
}
}
IL代码
IL_0001: ldstr "hello world!"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.5
IL_0009: ldstr "..."
IL_000e: call string [StringHelp]StringHelp.StringHelp::CutStr(string,
int32,
string)
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ldloc.0
IL_001a: ldc.i4.5
IL_001b: ldstr "..."
IL_0020: call string [StringHelp]StringHelp.StringHelp::CutStrNew(string,
int32,
string)
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: nop
IL_002b: ldtoken [StringHelp]StringHelp.StringHelp
IL_0030: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0035: ldstr "CutStr"
IL_003a: call instance class [mscorlib]System.Reflection.MethodInfo [mscorlib]System.Type::GetMethod(string)
IL_003f: ldnull
IL_0040: ldc.i4.3
IL_0041: newarr [mscorlib]System.Object
IL_0046: stloc.1
IL_0047: ldloc.1
IL_0048: ldc.i4.0
IL_0049: ldloc.0
IL_004a: stelem.ref
IL_004b: ldloc.1
IL_004c: ldc.i4.1
IL_004d: ldc.i4.5
IL_004e: box [mscorlib]System.Int32
IL_0053: stelem.ref
IL_0054: ldloc.1
IL_0055: ldc.i4.2
IL_0056: ldstr "..."
IL_005b: stelem.ref
IL_005c: ldloc.1
IL_005d: callvirt instance object [mscorlib]System.Reflection.MethodBase::Invoke(object,
object[])
IL_0062: call void [mscorlib]System.Console::WriteLine(object)
三种调用的方式是这样的:
1、string [StringHelp]StringHelp.StringHelp::CutStr(string, int32, string)
2、string [StringHelp]StringHelp.StringHelp::CutStrNew(string,
int32,
string)
3、instance object [mscorlib]System.Reflection.MethodBase::Invoke(object,
object[])
很明显,并没有使用反射,而是编译器的语法糖,使得调用方法更为简便。在编译时期,编译器会对代码作用域范围内可见的所有静态类,且带this 特征参数的方法进行搜索,
然后透明调用符合特征的方法。
那么效率如何呢
1、86 2、84 3、313
效率基本没有,差别,因为其实调用的方式都没有什么区别,而且性能比反射自然要好很多。