【问题标题】:C# Array Within Array-of-Structs Losing Contents结构数组中的 C# 数组丢失内容
【发布时间】:2017-05-19 19:05:04
【问题描述】:

在长时间使用 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
    ...
} 

这不是一个特别好的代码 sn-p,但是,在 RenderFontGlyph 函数中,我可以看到 bmp 数组在 RenderFontGlyph 函数返回时被正确分配,在检查 glyphs[idx] 变量时, bmp 恢复为空。

我很感激我可能正在做一些 n00bish 但已经有一段时间了。我是垃圾收集的受害者还是我很愚蠢?我突然想到该结构是按值而不是按引用传递给 RenderFontGlyph 函数的,但这也没有什么区别!

【问题讨论】:

  • structs,当按值传递时(这是默认值),正在被复制,因此您在方法中修改 glyphs[idx] 的副本,它对实例没有影响存储在数组中。通过引用传递值以使其工作。
  • @MarcinJuraszek,感谢您的评论。我再次尝试以 ref 传递,但是当 RenderFontGlyph 返回时,bmp 字段仍然为空 - 即使它在函数中正确初始化。

标签: c# arrays struct null


【解决方案1】:

我想到该结构是按值而不是按引用传递给 RenderFontGlyph 函数的,但这也没有区别!

是的,确实如此。您正在创建结构的副本,并将其传递给RenderFontGlyph。对该副本所做的任何更改都不会影响其他任何内容。

如果您改为通过引用传递它,它会有所作为,因为您将修改数组中的原始存储位置:

RenderFontGlyph(ref glyphs[idx]);

...

static void RenderFontGlyph(ref FontGlyph glyph)

或者您可以继续使用 value 参数,并根据 Leonardo 的回答让 RenderFontGlyph 返回您需要存储回数组中的修改后的值。

我当然不会说你是愚蠢,但你理解semantics of reference types and value types真的非常重要,尤其是如果您正在创建可变值类型。 (更糟糕的是,一个包含对可变引用类型的引用的可变值类型——在这种情况下是数组。你可以在不改变结构的情况下改变数组......如果你不小心,这一切都会变得非常混乱。)

除非您有充分的理由创建可变值类型,否则我强烈建议您不要这样做 - 就像我也建议不要公开公共字段一样。你几乎肯定应该将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];
    }
}

【讨论】:

  • 谢谢乔恩,我会试一试的。显然,有些事情是错过了
  • @weblar83:“通过 ref 并没有什么不同”——这不是真的。请参阅我更新的答案以获取证明它确实有效的完整示例。我们看不到您尝试执行的操作,因此我们不知道问题出在哪里,但 ref 工作正常。
【解决方案2】:

怎么样:

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])

【讨论】:

  • 真的不应该是struct。它在概念上是一个类,应该这样实现。
猜你喜欢
  • 2020-12-28
  • 1970-01-01
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
相关资源
最近更新 更多