您的位置:首页 > 编程语言 > C#

一起谈.NET技术,重新认识C#: 玩转指针

2011-08-29 19:30 666 查看
  许多文章并不鼓励在C#下使用指针开发,不过,本文偏偏要这样做。在大量尝试C#下使用指针开发之后,你会对C#有更深的认识。

  在说C#下的指针之前,需要提一下C++/CLI。C++/CLI 我们可以把它看作两部分:Native C++和 Managed C++,两者可以无缝结合。对C#,我们也可以把它看作两部分:Managed C# 和 Unmanaged C#。Managed C# 和 Unmanaged C# 是我杜撰的两个词,前者就是我们通常的C#,后者就是使用指针、Struct和非托管内存的C#。事实证明,Unmanaged C#也可以玩的十分优雅——它具有C语言的大部分特性,却比C语言好用的多。 C# 与 C++/CLI之间的对应关系见下图:

ImageClassHelper_Template.cs

1 using TPixel = System.Byte;
2 using TCache = System.Int32;
3 using TKernel = System.Int32;
4
5 using System;
6 using System.Collections.Generic;
7 using System.Text;
8
9 namespace Orc.SmartImage.Hidden
{
static class ImageClassHelper_Template
{
#region mixin

public unsafe delegate void ActionOnPixel(TPixel* p);
public unsafe delegate Boolean PredicateOnPixel(TPixel* p);

public unsafe static void ForEach(this UnmanagedImage<TPixel> src, ActionOnPixel handler)
{
TPixel* start = (TPixel*)src.StartIntPtr;
TPixel* end = start + src.Length;
while (start != end)
{
handler(start);
++start;
}
}

public unsafe static Int32 Count(this UnmanagedImage<TPixel> src, PredicateOnPixel handler)
{
TPixel* start = (TPixel*)src.StartIntPtr;
TPixel* end = start + src.Length;
Int32 count = 0;
while (start != end)
{
if (handler(start) == true) count++;
++start;
}
return count;
}

public unsafe static Int32 Count(this UnmanagedImage<TPixel> src, Predicate<TPixel> handler)
{
TPixel* start = (TPixel*)src.StartIntPtr;
TPixel* end = start + src.Length;
Int32 count = 0;
while (start != end)
{
if (handler(*start) == true) count++;
++start;
}
return count;
}

public unsafe static List<TPixel> Where(this UnmanagedImage<TPixel> src, PredicateOnPixel handler)
{
List<TPixel> list = new List<TPixel>();

TPixel* start = (TPixel*)src.StartIntPtr;
TPixel* end = start + src.Length;
while (start != end)
{
if (handler(start) == true) list.Add(*start);
++start;
}

return list;
}

public unsafe static List<TPixel> Where(this UnmanagedImage<TPixel> src, Predicate<TPixel> handler)
{
List<TPixel> list = new List<TPixel>();

TPixel* start = (TPixel*)src.StartIntPtr;
TPixel* end = start + src.Length;
while (start != end)
{
if (handler(*start) == true) list.Add(*start);
++start;
}

return list;
}

#endregion
}
}

  这样一来,就提供了ForEach扩展方法,可以通过指针直接访问具体的像素。同时,我也顺便实现了Count和Where两个扩展方法。Count和Where两个扩展方法同时提供了指针版本和非指针版本。

  然后,编写类 Rgb24ImageClassHelper:

Rgb24ImageClassHelper.cs

1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Orc.SmartImage
6 {
7 using TPixel = Rgb24;
8 using TCache = System.Int32;
9 using TKernel = System.Int32;

public static partial class Rgb24ImageClassHelper
{
#region include "ImageClassHelper_Template.cs"
#endregion
}
}

  编译之后,就可以通过Lambda表达式通过指针来访问 UnmanagedImage 实例中的像素。例子&性能测试为:

例子与性能测试代码

1 Rgb24Image rgb24 = new Rgb24Image(map);
2
3 // 将每个像素的Blue值改为 50
4
5 CodeTimer.Time("ForEachByLambdaWithPointer-" + imgName, 1, () =>
6 {
7 rgb24.ForEach((Rgb24* p) => { p->Blue = 50; });
8 Console.WriteLine(rgb24.Start->Blue);
9 });

CodeTimer.Time("ForEachByPointer-" + imgName, 1, () =>
{
Rgb24* start = rgb24.Start;
Rgb24* end = rgb24.Start + rgb24.Length;
while (start != end)
{
start->Blue = 50;
++start;
}
Console.WriteLine(rgb24.Start->Blue);
});

CodeTimer.Time("CountByLambdaWithPointer-" + imgName, 1, () =>
{
Console.WriteLine(rgb24.Count((Rgb24* p) => { return p->Blue > 50; }));
});

CodeTimer.Time("CountByLambdaWithValue-" + imgName, 1, () =>
{
Console.WriteLine(rgb24.Count((Rgb24 c) => { return c.Blue > 50; }));
});

CodeTimer.Time("WhereByLambdaWithPointer-" + imgName, 1, () =>
{
Console.WriteLine(rgb24.Where((Rgb24* p) => { return p->Blue > 50; }).Count);
});

CodeTimer.Time("WhereByLambdaWithValue-" + imgName, 1, () =>
{
Console.WriteLine(rgb24.Where((Rgb24 c) => { return c.Blue > 50; }).Count);
});

  测试结果:

测试结果

1 ForEachByLambdaWithPointer-5000_6000_24
2 50
3 Time Elapsed: 210ms
4 CPU Cycles: 333,752,386
5 Gen 0: 0
6 Gen 1: 0
7 Gen 2: 0
8
9 ForEachByPointer-5000_6000_24
50
Time Elapsed: 76ms
CPU Cycles: 116,868,697
Gen 0: 0
Gen 1: 0
Gen 2: 0

CountByLambdaWithPointer-5000_6000_24
0
Time Elapsed: 249ms
CPU Cycles: 425,180,283
Gen 0: 0
Gen 1: 0
Gen 2: 0

CountByLambdaWithValue-5000_6000_24
0
Time Elapsed: 484ms
CPU Cycles: 850,295,952
Gen 0: 0
Gen 1: 0
Gen 2: 0

WhereByLambdaWithPointer-5000_6000_24
0
Time Elapsed: 242ms
CPU Cycles: 425,229,156
Gen 0: 0
Gen 1: 0
Gen 2: 0

WhereByLambdaWithValue-5000_6000_24
0
Time Elapsed: 496ms
CPU Cycles: 855,667,758
Gen 0: 0
Gen 1: 0
Gen 2: 0

  可见:使用Lambda表达式通过指针来访问像素比使用指针直接访问像素慢,大概速度是后者的 1/2 - 1/3 。而使用Lambda表达式通过值来访问像素比使用Lambda表达式通过指针来访问像素要慢。大概速度是后者的1/2。虽然速度慢下来了,但对于性能不攸关的地方,这样处理还是值得的——使用Lambda表达式可以让代码更简洁更优雅!

  好了,现在,类C,OO,泛型/模板,Lambda表达式就全揉在一起了,至于具体怎么用,就看具体情况下的权衡取舍了。如果再玩玩Dynamic,大概会有更有趣的玩法。

  现在看来,C#真是太NB了!通吃啊!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: