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