在长时间的C++和Qt之后,我只是回到了C#。我目前被我认为非常简单的问题难倒了。
我有一个结构:
struct FontGlyph {
public uint codepoint;
public byte[] bmp;
...
}
以及一系列这样的结构:
FontGlyph[] glyphs = new FontGlyph[100];
现在,我有几个函数可以设置和修改结构中的字段:
static void ConvertFont(Font fnt) {
...
if (fnt.IsSpaceCharacter) {
glyphs[idx].codepoint = 0x00FF;
glyphs[idx].bmp = null;
}
else {
RenderFontGlyph(glyphs[idx]);
// glyphs[idx].bmp is NOT ok here - it is null!
}
...
}
static void RenderFontGlyph(FontGlyph glyph) {
...
glyph.bmp = new byte[256];
// bmp is fine here, and can be populated as a normal array
...
}
这不是一个特别好的代码片段,但是,在 RenderFontGlyph 函数中,我可以看到bmp
数组被正确分配,但当 RenderFontGlyph 函数返回时,在检查glyphs[idx]
变量时,bmp
回到 null。
我很感激我可能正在做一些 n00bish 的事情,但已经有一段时间了。我是垃圾收集的受害者还是愚蠢?我突然想到,该结构被传递到RenderFontGlyph
函数 by-value 而不是 by-ref 中,但这也没有区别!
我突然想到该结构被传递到 RenderFontGlyph 函数 by-value 而不是 by-ref 中,但这也没有区别!
嗯,是的,确实如此。您正在创建结构的副本,并将其传递给RenderFontGlyph
。对该副本所做的任何更改都不会影响其他任何内容。
如果改为通过引用传递它,它将有所不同,因为您将修改数组中的原始存储位置:
RenderFontGlyph(ref glyphs[idx]);
...
static void RenderFontGlyph(ref FontGlyph glyph)
或者你可以继续使用一个值参数,并根据达芬奇的答案,RenderFontGlyph
返回你需要存储回数组中的修改后的值。
我当然不会说你很愚蠢,但理解引用类型和值类型的语义真的非常重要,特别是当你正在创建可变值类型时。(更糟糕的是,包含对可变引用类型的引用的可变值类型 - 在本例中为数组。您可以在不改变结构的情况下改变数组...如果你不小心,这一切都可能会变得非常混乱。
除非你有很好的理由来创建可变值类型,否则我强烈建议不要这样做 - 就像我也建议不要公开公共字段一样。你几乎肯定应该把FontGlyph
建模作为一个类——对我来说,它不是一种自然的价值类型。如果您确实想将其建模为值类型,那么为什么不直接传入要呈现的代码点,并使该方法返回字形,而不是传入FontGlyph
呢?
glyphs[0] = RenderGlyph(codePoint);
由于您声称按引用传递对您不起作用,因此这里有一个完整的示例来证明它确实有效。您应该将其与您的代码进行比较,看看您做错了什么:
using System;
struct FontGlyph
{
public uint codepoint;
public byte[] bmp;
}
class Program
{
static void Main()
{
FontGlyph[] glyphs = new FontGlyph[100];
RenderFontGlyph(ref glyphs[0]);
Console.WriteLine(glyphs[0].bmp.Length); // 10
}
static void RenderFontGlyph(ref FontGlyph glyph)
{
glyph.bmp = new byte[10];
}
}
怎么样:
static void ConvertFont(Font fnt) {
...
if (fnt.IsSpaceCharacter) {
glyphs[idx].codepoint = 0x00FF;
glyphs[idx].bmp = null;
}
else {
glyphs[idx] = RenderFontGlyph(glyphs[idx]);
// glyphs[idx].bmp is NOT ok here - it is null!
}
...
}
static FontGlyph RenderFontGlyph(FontGlyph glyph) {
...
glyph.bmp = new byte[256];
// bmp is fine here, and can be populated as a normal array
...
return glyph;
}
或者像这样使用ref
:static void RenderFontGlyph(ref FontGlyph glyph)
然后像这样称呼它:RenderFontGlyph(ref glyphs[idx])