一次性上下文对象模式 [英] Disposable Context Object pattern

查看:137
本文介绍了一次性上下文对象模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介



我刚刚想到了一种新的设计模式。我想知道是否存在,如果不存在,为什么不(或为什么我不应该使用它)。



我正在使用OpenGL创建一个游戏。在OpenGL中,您经常要绑定事物 - 即将它们当作上下文一段时间,然后解除绑定。例如,您可以调用 glBegin(GL_TRIANGLES),然后绘制一些三角形,然后调用 glEnd()。我喜欢缩进所有的东西,所以很清楚它的开始和结束,但是我的IDE喜欢取消缩进它们,因为没有大括号。然后我以为我们可以做一些聪明的事情!它基本上是这样的:

  using(GL.Begin(GL_BeginMode.Triangles)){
//绘制东西
}

GL.Begin 返回一个特殊的 DrawBind 对象(具有内部构造函数),并实现 IDisposable ,以便它自动调用 GL.End()在块的末尾。这样一来,一切都保持一致,你不能忘记调用end()。



这个模式有名字吗?



通常当我使用使用时,您可以这样使用:

  using(var x = new Whatever()){
// do stuff with`x`
}

但是在这种情况下,我们不需要在我们的'used'对象上调用任何方法,所以我们不需要将它分配给任何东西,它不服务目的不是调用相应的最终功能。






示例



对于 Anthony Pegram ,谁想要一个真正的代码示例,我正在处理:



重构之前:

  public void Render 
{
_vao.Bind();
_ibo.Bind(BufferTarget.ElementArrayBuffer);
GL.DrawElements(BeginMode.Triangles,_indices.Length,DrawElementsType.UnsignedInt,IntPtr.Zero);
BufferObject.Unbind(BufferTarget.ElementArrayBuffer);
VertexArrayObject.Unbind();
}

重构后:

  public void Render()
{
using(_vao.Bind())
using(_ibo.Bind( BufferTarget.ElementArrayBuffer))
{
GL.DrawElements(BeginMode.Triangles,_indices.Length,DrawElementsType.UnsignedInt,IntPtr.Zero);
}
}

请注意,对象返回的第二个好处是 _ibo.Bind 还记得我要解除绑定的BufferTarget。它还引用了您对 GL.DrawElements 的注意,这是该函数中唯一重要的语句(可以做一些明显的事情),并隐藏了冗长的解除绑定语句。 p>

我猜这个缺点是我不能用这个方法来交换缓冲区目标。我不知道我什么时候想要的,但是我必须保留对绑定对象的引用,并手动调用Dispose,或者手动调用end函数。






命名



如果没有人对象,我正在复制这个一次性上下文对象(DCO)成语






问题



JasonTrue 提出了一个好点,在这种情况下(OpenGL缓冲区)嵌套使用语句将无法正常工作,因为一次只能绑定一个缓冲区。我们可以通过扩展绑定对象来使用堆栈来解决这个问题:

  public class BufferContext:IDisposable 
{
private readonly BufferTarget _target;
private static readonly Dictionary< BufferTarget,Stack< int>> _handles;

static BufferContext()
{
_handles =新字典< BufferTarget,Stack< int>();
}

内部BufferContext(BufferTarget target,int handle)
{
_target = target;
if(!_handles.ContainsKey(target))_handles [target] = new Stack< int>();
_handles [target] .Push(handle);
GL.BindBuffer(target,handle);
}

public void Dispose()
{
_handles [_target] .Pop();
int handle = _handles [_target] .Count> 0? _handles [_target] .Peek():0;
GL.BindBuffer(_target,handle);
}
}

编辑:刚刚注意到这个问题。之前,如果你没有 Dispose()的上下文对象没有真正的后果。上下文不会切换回任何它。现在,如果您忘记将其处理在某种循环中,那么您将收到一个 stackoverflow 。也许我应该限制堆栈大小...

解决方案

与Asp.Net MVC与HtmlHelper类似的策略。请参阅 http://msdn.microsoft。 com / en-us / library / system.web.mvc.html.formextensions.beginform.aspx using(Html.BeginForm()){....}



因此,至少有一个先例将这种模式用于除了对于非托管资源(如文件句柄,数据库或网络)的显着需要连接,字体等。我不认为它有一个特殊的名字,但在实践中,似乎是作为C ++习语的C#成语,资源获取是初始化。



当您打开文件时,您正在获取并保证处理文件上下文;在你的例子中,你所获得的资源是一个绑定上下文,用你的话来说。虽然我听说过用于描述广泛类别的处理模式或使用模式,但本质上确定性清理就是你所说的;你正在控制一生的对象。



我不认为这真的是一个新模式,在你的用例中唯一的原因是显然,你所依赖的OpenGL实现并没有特别的努力来匹配C#的成语,这需要你建立自己的代理对象。



唯一的事情我'担心是否有任何不明显的副作用,例如,如果你有一个嵌套的上下文,那里有类似的使用在你的块中更深入地构建(或调用堆栈)。


Introduction

I just thought of a new design pattern. I'm wondering if it exists, and if not, why not (or why I shouldn't use it).

I'm creating a game using an OpenGL. In OpenGL, you often want to "bind" things -- i.e., make them the current context for a little while, and then unbind them. For example, you might call glBegin(GL_TRIANGLES) then you draw some triangles, then call glEnd(). I like to indent all the stuff inbetween so it's clear where it starts and ends, but then my IDE likes to unindent them because there are no braces. Then I thought we could do something clever! It basically works like this:

using(GL.Begin(GL_BeginMode.Triangles)) {
   // draw stuff
}

GL.Begin returns a special DrawBind object (with an internal constructor) and implements IDisposable so that it automatically calls GL.End() at the end of the block. This way everything stays nicely aligned, and you can't forget to call end().

Is there a name for this pattern?

Usually when I see using used, you use it like this:

using(var x = new Whatever()) {
   // do stuff with `x`
}

But in this case, we don't need to call any methods on our 'used' object, so we don't need to assign it to anything and it serves no purpose other than to call the corresponding end function.


Example

For Anthony Pegram, who wanted a real example of code I'm currently working on:

Before refactoring:

public void Render()
{
    _vao.Bind();
    _ibo.Bind(BufferTarget.ElementArrayBuffer);
    GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
    BufferObject.Unbind(BufferTarget.ElementArrayBuffer);
    VertexArrayObject.Unbind();
}

After refactoring:

public void Render()
{
    using(_vao.Bind())
    using(_ibo.Bind(BufferTarget.ElementArrayBuffer))
    {
        GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
    }
}

Notice that there's a 2nd benefit that the object returned by _ibo.Bind also remembers which "BufferTarget" I want to unbind. It also draws your atention to GL.DrawElements, which is really the only significant statement in that function (that does something noticeable), and hides away those lengthy unbind statements.

I guess the one downside is that I can't interlace Buffer Targets with this method. I'm not sure when I would ever want to, but I would have to keep a reference to bind object and call Dispose manually, or call the end function manually.


Naming

If no one objects, I'm dubbing this Disposable Context Object (DCO) Idiom.


Problems

JasonTrue raised a good point, that in this scenario (OpenGL buffers) nested using statements would not work as expected, as only one buffer can be bound at a time. We can remedy this, however, by expanding on "bind object" to use stacks:

public class BufferContext : IDisposable
{
    private readonly BufferTarget _target;
    private static readonly Dictionary<BufferTarget, Stack<int>> _handles;

    static BufferContext()
    {
        _handles = new Dictionary<BufferTarget, Stack<int>>();
    }

    internal BufferContext(BufferTarget target, int handle)
    {
        _target = target;
        if (!_handles.ContainsKey(target)) _handles[target] = new Stack<int>();
        _handles[target].Push(handle);
        GL.BindBuffer(target, handle);
    }

    public void Dispose()
    {
        _handles[_target].Pop();
        int handle = _handles[_target].Count > 0 ? _handles[_target].Peek() : 0;
        GL.BindBuffer(_target, handle);
    }
}

Edit: Just noticed a problem with this. Before if you didn't Dispose() of your context object there wasn't really any consequence. The context just wouldn't switch back to whatever it was. Now if you forget to Dispose of it inside some kind of loop, you're wind up with a stackoverflow. Perhaps I should limit the stack size...

解决方案

A similar tactic is used with Asp.Net MVC with the HtmlHelper. See http://msdn.microsoft.com/en-us/library/system.web.mvc.html.formextensions.beginform.aspx (using (Html.BeginForm()) {....})

So there's at least one precedent for using this pattern for something other than the obvious "need" for IDisposable for unmanaged resources like file handles, database or network connections, fonts, and so on. I don't think there's a special name for it, but in practice, it seems to be the C# idiom that serves as the counterpart to the C++ idiom, Resource Acquisition is Initialization.

When you're opening a file, you're acquiring, and guaranteeing the disposal of, a file context; in your example, the resource you're acquiring is a is a "binding context", in your words. While I've heard "Dispose pattern" or "Using pattern" used to describe the broad category, essentially "deterministic cleanup" is what you're talking about; you're controlling the lifetime the object.

I don't think it's really a "new" pattern, and the only reason it stands out in your use case is that apparently the OpenGL implementation you're depending on didn't make a special effort to match C# idioms, which requires you to build your own proxy object.

The only thing I'd worry about is if there are any non-obvious side effects, if, for example, you had a nested context where there were similar using constructs deeper in your block (or call stack).

这篇关于一次性上下文对象模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆