传递颜色的策略(避免参考?)

时间:2013-01-03 10:25:27

标签: c# colors struct ref

我在老化的Windows应用程序中重构代码,我遇到了各种各样的模式,我不确定我喜欢:类有全局颜色变量,如下所示:

private Color myForegroundColor = Color.Azure;
private Color myBackgroundColor = Color.Empty;
// ...etc. 

有很多这些,并且它们被传递给负责设置UI的某些部分的方法。

我认为Color是一个结构,因此每个颜色都由ref传递,以避免每次调用方法时都创建它的新副本。 IE之类的东西:

// Avoid creating a copy of myForgroundColor inside SetUpButton():
MyHelperClass.SetUpButton(ref myForegroundColor); 

我不禁觉得在这个班级和相关课程中使用ref是不好的。这感觉就像一个“code smell”,虽然我无法真正理解为什么。

我在类似的问题上看过几篇帖子,其中的建议包括“使用包含颜色的类,然后将其作为值类型传递”,但目前还不完全清楚它是怎么回事最好这样做。

我想做的是创建类似于以下内容的内容:

public class ColorContainer
{
    public UiSettingsContainer()
    {
        MyColor = Color.Black;
        MyNextColor = Color.Blue;
        // ..etc...
    }

    public Color MyColor { get; private set; }
    // ...etc....
}

这可以让我保持对颜色的控制,但对记忆的影响对我来说有点不清楚;如果我创建了这个类的一个实例并将其传递给需要有关所包含颜色的信息的方法,那么一旦实现方法使用,就不会创建color(它是结构体)的副本它?

我是否正确地假设此代码会创建一个新副本,因此效果较差......

// Assumption: This creates a new copy of color in memory.
public void SetSomeColor(Color col){ 
    someComponent.color = col; 
}

// Calling it:
SetSomeColor(myColorContainerInstance.MyColor);

...比这个代码只能使用现有的struct?:

// Question: Does this avoid creating a new copy of MyColor in memory?
public void SetSomeColor(ColorContainer container){ 
    someComponent.color = container.MyColor; 
}

// Calling it:
SetSomeColor(myColorContainerInstance);

我目前正倾向于使用类似于以下内容的解决方案,其中我将颜色收集在一个单独的类中并稍微重新组织代码,但继续使用ref。但是,在这种情况下,MyColor必须是ColorContainer中的公共字段,这意味着我对谁可以设置其值的控制较少:

// Assumption: This creates a new copy of color in memory.
public void SetSomeColor(ref Color col){ 
    someComponent.color = col; 
}

// Calling it:
SetSomeColor(ref myColorContainerInstance.MyColor);

这是一个很好的解决方案,还是有更好的策略来处理这样的资源?

3 个答案:

答案 0 :(得分:4)

整个事情闻起来像premature optimization,特别是链接的第3和第4部分,所以......

另一个解决方案是删除引用,并在需要时复制Color结构。结构本身不是太大(4个byte成员和4个bool成员),除非你调用的代码每秒改变颜色数百万次,所以所需的时间和内存不是问题

答案 1 :(得分:1)

“老虎机”类比长期以来一直是我最喜欢的类型之一。每个方法参数(也将分配的右手视为参数)是一个槽。每个插槽必须填充正确大小和“形状”(类型)的东西,以便调用方法(或要处理的分配)。

如果你的方法需要一个ref Color,那么你将使用内存中Color结构的任何大小指针填充插槽。当然我并不是指C风格指针,但它仍然是同一类 - 它是一个数字,表示您打算使用的资源的位置,即使它没有在代码中表示。在Color(没有参考)的情况下,您将使用Color结构本身填充它。

根据您编译的平台,您传递的值(通过ref传递Color)的长度为32位或64位。颜色结构(System.Windows.Media.Color)本身长度只有32位(如果你使用System.Drawing.Color,则长度为64位)这使得它成为一个没有吸引力的命题 - 使得平均情况场景完全相同(在指针的副本数量和加载到堆栈上的东西的大小的条款)通过值传递结构 - 更好地仅在64位结构/ 32位平台配对中更好,更糟糕的是仅在32位结构/ 64位平台中配对。即使在尝试使用相同的实例之后,struct的实际值仍将被复制到其目标槽中。

现在,在一个类中将颜色捆绑在一起(默认情况下通过ref传递)将更改此图片。如果您的类包含3种颜色,则其中包含96位(或192位)颜色数据,最多传递64位信息,以便在内存中找到该信息的正确“包”的位置。即使在包装后,颜色仍会被复制到目标位置;但是现在我们已经增加了从ldfld(加载字段)/ call(调用预先解析的方法 - 属性访问)+ ldfld / callvirt(调用运行时解析)的开销方法 - 属性访问)+ ldfld实际获取值。从性能的角度来看,这根本不会对你有所帮助,除非你打算传递大量数据然后不再使用它。

长话短说 - 除非你试图实现一些逻辑分组的颜色信息,否则不要打扰。当弹出堆栈帧时,堆栈上的值类型会立即被清除,所以除非你的程序运行在系统总内存的8个字节以内,否则你通常不会使用by ref方法。颜色集合的包装类可能会使代码更清晰/更好,但效率更高。

答案 2 :(得分:1)

通过ref传递结构通常是一件好事,除非(1)想要传递值语义,或者(2)结构很小并且可以使用按值传递语义。< / p>

如果一个人经常想要将一些变量(可能是结构本身)作为一个组,那么声明一个透明的结构类型来保存它们,然后通过ref传递它可能会有所帮助。 / p>

请注意,通过ref传递结构与通过值传递类引用的成本基本相同。编写一个类来保存结构,纯粹是为了避免使用ref参数,不容易成为性能获胜者。在某些情况下,拥有类型

可能很有用
class MutableHolder<T>
{ public T Value; }

然后可以将引用语义应用于任何结构类型,但我只建议如果需要在当前范围之外保留引用。如果ref足够,则应使用ref