Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 一次性上下文对象模式 介绍_C#_Design Patterns_Idisposable - Fatal编程技术网

C# 一次性上下文对象模式 介绍

C# 一次性上下文对象模式 介绍,c#,design-patterns,idisposable,C#,Design Patterns,Idisposable,我刚想到一种新的设计模式。我想知道它是否存在,如果不存在,为什么不(或者为什么我不应该使用它) 我正在使用OpenGL创建一个游戏。在OpenGL中,您通常希望“绑定”事物——即,将它们作为当前上下文一段时间,然后解除绑定。例如,您可以调用glBegin(GL\u三角形)然后绘制一些三角形,然后调用glEnd()。我喜欢将所有内容缩进,这样就可以清楚地知道它们的开始和结束,但是我的IDE喜欢取消它们的缩进,因为没有大括号。然后我想我们可以做些聪明的事!它基本上是这样工作的: using(GL.B

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

我正在使用OpenGL创建一个游戏。在OpenGL中,您通常希望“绑定”事物——即,将它们作为当前上下文一段时间,然后解除绑定。例如,您可以调用
glBegin(GL\u三角形)
然后绘制一些三角形,然后调用
glEnd()
。我喜欢将所有内容缩进,这样就可以清楚地知道它们的开始和结束,但是我的IDE喜欢取消它们的缩进,因为没有大括号。然后我想我们可以做些聪明的事!它基本上是这样工作的:

using(GL.Begin(GL_BeginMode.Triangles)) {
   // draw stuff
}
using(var x = new Whatever()) {
   // do stuff with `x`
}
@using (Html.BeginForm()) {
    <div>...</div>
}
GL.Begin
返回一个特殊的
DrawBind
对象(带有内部构造函数),并实现
IDisposable
,以便它在块的末尾自动调用
GL.End()
。这样,所有内容都可以很好地对齐,并且您不会忘记调用end()

这个图案有名字吗

通常,当我看到
使用
时,您会这样使用它:

using(GL.Begin(GL_BeginMode.Triangles)) {
   // draw stuff
}
using(var x = new Whatever()) {
   // do stuff with `x`
}
@using (Html.BeginForm()) {
    <div>...</div>
}
但是在这种情况下,我们不需要调用“used”对象上的任何方法,因此我们不需要将其分配给任何对象,它除了调用相应的end函数之外没有其他用途


实例 例如,谁想要我目前正在研究的代码的真实示例:

重构前:

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);
    }
}
重构后:

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);
    }
}
请注意,还有第二个好处是,
\u ibo.Bind
返回的对象还记得我要解除绑定的“BufferTarget”。它还将您的atention绘制到
GL.drawerelements
,这实际上是该函数中唯一有意义的语句(做了一些值得注意的事情),并隐藏了那些冗长的unbind语句

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


命名 如果没有人反对,我就配音这个一次性上下文对象(DCO)习惯用法


问题 提出了一个很好的观点,在这个场景中,使用语句嵌套的(OpenGL缓冲区)不会像预期的那样工作,因为一次只能绑定一个缓冲区。但是,我们可以通过扩展“绑定对象”来使用堆栈来解决这一问题:

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);
    }
}
公共类缓冲上下文:IDisposable
{
私有只读缓冲区目标_目标;
私有静态只读字典句柄;
静态缓冲上下文()
{
_handles=newdictionary();
}
内部BufferContext(BufferTarget,int句柄)
{
_目标=目标;
如果(!_handles.ContainsKey(target))_handles[target]=新堆栈();
_句柄[目标]。推(句柄);
总账绑定缓冲区(目标、句柄);
}
公共空间处置()
{
_句柄[_target].Pop();
int handle=\u handles[\u target]。计数>0?\u handles[\u target]。Peek():0;
总账绑定缓冲区(_目标,句柄);
}
}
编辑:刚刚注意到这个问题。以前,如果您没有对上下文对象进行
Dispose()
,实际上不会产生任何后果。上下文不会切换回它是什么。现在,如果你忘了在某种循环中处理它,那么你就有了堆栈溢出。也许我应该限制堆栈大小…

ASP.NET/MVC使用此(可选)模式来呈现
元素的开头和结尾,如下所示:

using(GL.Begin(GL_BeginMode.Triangles)) {
   // draw stuff
}
using(var x = new Whatever()) {
   // do stuff with `x`
}
@using (Html.BeginForm()) {
    <div>...</div>
}
@使用(Html.BeginForm()){
...
}
这与您的示例类似,因为您不使用IDisposable的值,而使用它的一次性语义。我从来没有听说过这个名字,但我以前在其他类似场景中使用过这种东西,除了理解如何利用
IDisposable
块之外,从来没有把它看作是一件别的事情,类似于我们如何通过实现
IEnumerable
来利用
foreach
语义。ASP.NET/MVC使用这个(可选)模式来呈现
元素的开头和结尾,如下所示:

using(GL.Begin(GL_BeginMode.Triangles)) {
   // draw stuff
}
using(var x = new Whatever()) {
   // do stuff with `x`
}
@using (Html.BeginForm()) {
    <div>...</div>
}
@使用(Html.BeginForm()){
...
}

这与您的示例类似,因为您不使用IDisposable的值,而使用它的一次性语义。我从来没有听说过这个名字,但我以前在其他类似场景中使用过这种东西,除了理解如何利用
IDisposable
块之外,从来没有把它看作是一件别的事情,类似于我们如何通过实现
IEnumerable

来利用
foreach
语义,Asp.Net MVC和HtmlHelper也使用了类似的策略。请参阅(
使用(Html.BeginForm()){……}

因此,对于非托管资源(如文件句柄、数据库或网络连接、字体等)显然“需要”IDisposable之外的其他内容,使用此模式至少有一个先例。我不认为它有一个特殊的名字,但在实践中,它似乎是C语言成语,作为C++习语的对应物,资源获取是初始化。 当您打开一个文件时,您正在获取并保证对文件上下文的处理;在您的示例中,用您的话来说,您正在获取的资源是“绑定上下文”。虽然我听过“Dispose模式”或“Using模式”用来描述这个大的类别,但本质上“确定性清理”就是您所说的;您正在控制对象的生命周期

我不认为这是一个真正的“新”模式,它在您的用例中脱颖而出的唯一原因是,显然您所依赖的OpenGL实现没有特别努力匹配C#习惯用法,这需要您构建自己