每周源代码16 – 胶布版(Duct Tape Edition)

[原文发表地址] The Weekly Source Code 16 - Duct Tape Edition

[原文发表时间] 2008-02-20 22:01

      几周前,我采访了Panic合伙人,Mac开发人员之一的Steven Frank(博客),他也是我的大学校友。自那次采访之后,我偶然发现了最近发布的NSDuctTape项目。首先,对一个叫做胶布(Duct Tape)的项目,你怎么会不喜欢呢?其次,我只要听到某些代码可以解决不一致或无法连接的东西,都会去看看的。就是说,如果有怪人承诺要将两样事物连接起来,我就超级想看一下!

NSDuctTape是一个niche,不过如果你想在Mac上使用Mono编写.NET代码,并且你想访问Objective C cocoa库,这儿就是你的一站式商店。(注意:如果你想下载他的源代码,你可能要把文件一个个拉出来,因为zip包里的Mac文件有和文件夹同名的,而Windows并不喜欢这样。)

所以,亲爱的读者,我在这里奉上第16篇“每周源代码”,之后还会继续。这是我这周在读的一些源代码。

代码的作者Dave还没有看过Mono对Linq的支持,不过他使用了C#3.0的功能来创建他自己的LINQ-lite 辅助函数。我觉得这很聪明。

  
 internal static class Enumerable
 {
  public static 
 IEnumerable Select(IEnumerable list, Converter convert)
  
 {
  foreach (TInput value in 
 list)
  
 yield return convert(value);
  }
  public static IEnumerable SelectMany(IEnumerable list, 
 Converter> convert)
  
 {
  foreach (TInput value in 
 list)
  
 foreach (TOutput converted in 
 convert(value))
  
 yield return converted;
  }
  public static IEnumerable Where(IEnumerable list, 
 Predicate predicate)
  
 {
  foreach (T value in 
 list)
  if 
 (predicate(value))
  
 yield return value;
  }
  public static List ToList(IEnumerable 
 list)
  {
  List 
 result = list as List;
  return 
 result ?? new List(list);
  }
  public static T[] ToArray(IEnumerable 
 list)
  {
  
 return ToList(list).ToArray();
  }
 }

因为他深刻的”thunking”(思考)(虽然并非确切术语,但我喜欢这样说)使用到了非托管代码,这就需要处理分配和释放的问题,所以他用我最喜欢的.NET BCL 模型和Idisposable创建了一个HGlobal封装类。堪称经典,简单有效。

 ...snip...
  
 public void Dispose() 
 { 
  if (_hGlobal != IntPtr.Zero) 
  Marshal.FreeHGlobal(_hGlobal); 
  _hGlobal = IntPtr.Zero; 
 }
  
 private DisposableHGlobal(IntPtr hGlobal) 
 { 
  _hGlobal = hGlobal; 
 }
  
 public static DisposableHGlobal StructureToHGlobal(T value) 
  where T : struct 
 { 
  DisposableHGlobal result = new DisposableHGlobal(Marshal.SizeOf(value)); 
  Marshal.StructureToPtr(value, result.ToIntPtr(), false); 
  return result; 
 } 
 ...snip...

最后,在他应用程序管理的“wrapper”中,通过把System.Types做技术处理,将他们一 一用ObjectiveC注册。这种互操作是一种方式,表示他选择把.NET 类型作为Objective C 类来呈现。

 public static void Run(string nibFile, IEnumerable exposedTypes)
 {
  ObjectiveCClass nsAutoReleasePoolClass = ObjectiveCClass.GetClass("NSAutoreleasePool");
  IntPtr autoReleasePool = nsAutoReleasePoolClass.Instantiate();
  ObjectiveCMethods.SendMessage(autoReleasePool, ObjectiveCMethods.SelectorFromString("init"));
  try
  {
  IntPtr process = IntPtr.Zero;
  GetCurrentProcess(ref process);
  TransformProcessType(ref process, ProcessType.ForegroundApplication);
  SetFrontProcess(ref process);
  
  Registrar.Initialize();
  
  foreach (Type type in exposedTypes)
  {
  ObjectiveCNameAttribute attribute = MemberInfoUtility.GetCustomAttribute(type);
  Registrar.RegisterClass(attribute != null ? attribute.Name : type.Name, type);
  }
  
  ObjectiveCClass nsBundleClass = ObjectiveCClass.GetClass("NSBundle");
  IntPtr name = NativeString.StringToNativeString(nibFile);
  ObjectiveCClass nsDictionaryClass = ObjectiveCClass.GetClass("NSDictionary");
  IntPtr key = NativeString.StringToNativeString("NSOwner");
  ObjectiveCClass nsApplicationClass = ObjectiveCClass.GetClass("NSApplication");
  IntPtr sharedApplication = ObjectiveCMethods.SendMessage(nsApplicationClass.ToIntPtr(), ObjectiveCMethods.SelectorFromString("sharedApplication"));
  IntPtr nsDictionary = ObjectiveCMethods.SendMessage(nsDictionaryClass.ToIntPtr(), ObjectiveCMethods.SelectorFromString("dictionaryWithObject:forKey:"), sharedApplication, key);
  IntPtr zone = ObjectiveCMethods.SendMessage(sharedApplication, ObjectiveCMethods.SelectorFromString("zone"));
  ObjectiveCMethods.SendMessage(nsBundleClass.ToIntPtr(), ObjectiveCMethods.SelectorFromString("loadNibFile:externalNameTable:withZone:"), name, nsDictionary, zone);
  
  ObjectiveCMethods.SendMessage(sharedApplication, ObjectiveCMethods.SelectorFromString("run"));
  }
  finally
  {
  ObjectiveCMethods.SendMessage(autoReleasePool, ObjectiveCMethods.SelectorFromString("release"));
  autoReleasePool = IntPtr.Zero;
  }
 }

在RegisterClass里他为每一个.NET类都创建了一个Objective C类,并简洁的做了类定义和赋值操作。他在两处都使用了“反射”技术,反射.NET类型,方法等,并且动态的在ObjectiveC端创建了同种类型和方法等。

怪异但很有趣!