Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2358286
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: WINDOWS

2009-07-07 08:28:27

在写代码时碰到这样一种情形:
internal static Image GetImage(XmlElement img_elem)
{
   ...
   using(MemoryStream mem_stream = new MemoryStream(img_bytes) )
   {
        Image img = Image.FromStream(mem_stream);
        return img;
   }

    ...
   return null;
}

如果调用者只是想从一个XML节点中保存的序列化过的Image对象, 上述函数可以满足要求, 在内部需要借助于MemoryStream, 这是个实现了IDispose接口的类, 需要关心资源的释放.

但稍后, 我发现有另一个需求, 在另一处的调用者需要得到内部的 MemoryStream从而能把原始的图像流保存到磁盘文件, img有Save方法, 但那不保证得到的是最初的文件流的内容. 这个需求面临几个问题
* 从GetImage中额外返回 MemoryStream, 返回值已经被使用, 只能通过out/ref 参数的办法
* 但返回MemoryStream带来另一个问题: 资源释放的责任被强加给了caller, 更不说如果GetImage在中途抛出异常时, caller很可能就会漏掉对 MemoryStream的 Dispose调用. 而内部的using是异常安全的.

考虑caller对 MemoryStream的需求的特征:
* 该资源是在callee 中产生并销毁的, 生存周期仅在callee中的一段时间. 而caller也仅需要在函数返回后只使用一次即释放, 并不需要长期持有该资源, 不属于save and use, 而是get-use-discard
* caller并不想关心该资源管理
* callee已经很好地管理了该资源

需要一个办法, 在callee运行过程中, 在资源的活动时间之内, 让caller能得到短时的控制权使用该资源.
回调!

internal static Image GetImage(XmlElement img_elem, ParameterizedThreadStart stream_callback)
{
  ...
  using(MemoryStream mem_stream = new MemoryStream(img_bytes) )
  {
    Image img = Image.FromStream(mem_stream);
    if(stream_callback != null) stream_callback(mem_stream);
    return img;
  }

  ...
  return null;
}

不需要该资源的调用者可以传递null保持原来的行为. 需要该资源的调用者只需要提供一个匿名的回调函数:
using (FileStream fs = new FileStream(fname, FileMode.Open, FileAccess.Write))
{
  ParameterizedThreadStart callback =
      delegate(object obj)
      {
      MemoryStream stream = obj as MemoryStream;
      ForceAssert.AlwaysAssert(stream != null,
          "GetImage's ParameterizedThreadStart parameter is not MemoryStream");
      stream.Position = 0;
      stream.WriteTo(fs);
      };
  Image img = GetImage(image_elem, callback);
}

需要小心约定并遵循的是传递给ParameterizedThreadStart 回调函数的第一个参数.

如果你用过Image, 或许你已经看出上面代码里潜在的问题: Image对象产生之后, 对原始的File或MemoryStream仍有着后续的需求, 如果这些资源被释放, 当这些后续的需求产生的时候, 会有异常抛出. 这是另一个话题.
阅读(1290) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~