【问题标题】:Strange behavior with C# enumsC# 枚举的奇怪行为
【发布时间】:2012-11-15 15:32:02
【问题描述】:

我正在这里开发一个游戏,并发现了这个相当有趣的错误。假设你有这个枚举:

public enum ItemType 
{
    Food,
    Weapon,
    Tools,
    Written,
    Misc
};

一个基类

public class BaseItem
{
    public string Name = "Default Name";

    public ItemType Type = ItemType.Misc;
}

以及它的两个实例:

Ins1 = new BaseItem
{
   Name = "Something",
   Type = ItemType.Food
};

Ins2 = new BaseItem
{
   Name = "Something too",
   Type = ItemType.Tools
}

这就是发生在我身上的事情:第一个实例的类型将保持在基类中预初始化的状态,即使我在其构造函数中指定我希望类型为 Food。 第二个实例的类型将正确设置为工具。

如果,我在食物之前添加了一个新的枚举值,例如:

public enum ItemType 
{
    Nothing,
    Food,
    Weapon,
    Tools,
    Written,
    Misc
};

那么第一个实例的类型将如预期的那样是食物。第二个实例的类型也是正确的。

是什么导致了这种行为?简而言之,在构造函数中将类型设置为枚举的第一个值的所有实例实际上将返回到它们在 BaseItem 定义中的值。 在第一个枚举值之前添加一个额外的值显然可以解决问题;但这是错误的,所以我想知道是什么导致了这个问题。

谢谢!

--- 稍后编辑 --- 如果这有帮助:不对 BaseItem 内的“Type”字段进行任何初始化,只留下大括号构造函数进行初始化,则一切正常,无需向枚举添加“Nothing”值。

对此感到抱歉;经过更多的挖掘,这似乎是一个仅限 Unity 的错误。其他一些人也遇到过。我已经解决了这个问题;每个人都得到我的投票,我会添加我自己的答案;也许其他一些 Unity 用户会找到它。非常感谢您的帮助和关注!

【问题讨论】:

  • 你没有任何构造函数。
  • 您的代码对我来说运行良好。请告诉我们SSCCE
  • 首先,使用字段的类型名称是个坏主意,因为之前已经定义了 System.Type 类。尝试重命名它并查看。明确定义构造函数也很好。
  • 检查 VS 项目设置,枚举是否定义在与类/代码块相同的程序集中?在此处发布之前,您是否删除了任何常量数值/等?
  • @SLaks 关于构造函数:当用户代码为非静态类提供零个实例构造函数时,编译器将发出一个无参数实例构造函数(非抽象类为public) ,所以这不是问题。

标签: c# class enums


【解决方案1】:

如果有其他 Unity 用户在这里搜索信息,认为这是一些 .NET 行为问题: 看来这是一个仅限 Unity 的错误。其他人也遇到过。只是避免在类本身中为这类东西进行任何类型的初始化,或者使用属性。

这绝对不是 Microsoft .NET 问题,也不是 Mono .NET 问题(您可以随时启动 Mono 项目,使用相同的代码,它会正常工作)。

【讨论】:

  • 您应该将自己的答案设置为您问题的可接受答案。
  • 我试过了,但是 StackOverflow 不允许我这样做,除非我再等一天。
【解决方案2】:

如果声明枚举 ItemType 的代码位于另一个程序集(项目)中,而不是声明 BaseItem 类的代码或引入 Ins1Ins2 变量的代码,那么它至关重要当您更改 ItemType 枚举的定义时,您重新编译 所有 程序集。

【讨论】:

  • 他们在同一个项目中,所以这不是问题:(
  • @BogdanMarginean 您仍然需要停止运行/调试过程,重新编译,并从头开始,当您对枚举 ItemType 进行更改时。否则,源代码的常量可能与编译后的 IL 代码不同,这会导致奇怪的调试行为。
【解决方案3】:

只是有一个想法。不确定它是否会起作用。似乎 BaseItem 的构造函数在您初始化后被调用。尝试放置一个断点并查看何时调用 BaseItem 的构造函数。或者您可以尝试在设置名称和类型之前显式调用 BaseItem 的构造函数。

【讨论】:

  • 主构造函数在大括号之前被调用;此外,如果有帮助,基类还有另一个字段:IsConsumable,在 BaseItem 中初始化为 False,并在第一个实例中重新初始化为 True;第一个实例中的枚举值是错误的,但 IsConsumable 具有正确的值 True。
【解决方案4】:

这只是一个猜测,但是,由于 Enum 的第一个值等于 0,可能是在定义中设置默认值(ItemType.Misc 或 5)加上使用类型初始化器设置的组合值恢复到没有默认值(ItemType.Food 或 0)时的值会导致您指定的默认值(ItemType.Misc)被保留。

顺便说一句,这很重要,您实际上并没有定义构造函数。语法

Ins1 = new BaseItem
{
   Name = "Something",
   Type = ItemType.Food
};

... 不是对构造函数的调用,而是对类型初始化程序的调用。是的,默认构造函数被调用,但该构造函数中没有任何反应。

我敢打赌,如果您实际使用构造函数并在那里设置 ItemType 的默认值,您会看到这个问题消失了。比如:

public BaseItem()
{
    this.Type = ItemType.Misc;
}

虽然我不记得阅读过任何明确说明这一点的内容,但我认为您不应该使用您使用的语法为公共属性或字段设置默认值。如果需要设置,请在构造函数中设置,以便轻松覆盖。您使用的语法更适合私有支持字段,而不是可以从类外部设置的内容。

【讨论】:

  • 添加 BaseItem() 构造函数没有提供预期的结果;奇怪的是,只有在大括号构造函数中设置枚举的第一个值时,才会使实例恢复为 BaseItem 中的值。
  • 不显式给出构造函数是没有问题的。保证事情按以下顺序发生:(1) 字段初始值设定项将值设置为Misc。 (2) 默认构造函数运行(在我们的例子中什么都不做)。 (3) 对象初始化器(new 表达式)中的赋值运行并将值设置为Food。所以它最终是Food。它由规范定义。
  • 我已经更新了我的帖子;删除 BaseItem 内部的任何类型的初始化都有效。
【解决方案5】:

这段代码应该可以正常工作。也许您应该尝试发布一个重现您的问题的最小程序,因为它必须是由其他原因引起的。

您应该注意一件事。您使用的不是构造函数,而是对象初始化。主要区别在于,当您使用对象初始值设定项时,生成的代码首先调用对象的默认构造函数,然后设置属性/字段。

所以这段代码:

var myItem = new BaseItem
{
    Name = "something",
    Type = ItemType.Misc
}

其实等价于这个:

var myItem = new BaseItem();
myItem.Name = "something";
myItem.Type = ItemType.Misc;

您可能想要的是定义一个构造函数,如下所示:

class BaseItem
{
    public BaseItem(string name, ItemType type)
    {
        this.Name = name;
        this.Type = type;
    }

    // ...
}

然后像这样使用它:

var myItem = new BaseItem("something", ItemType.Misc);

我猜想使用这种更明确的方法可能会间接解决您的错误,并且至少可能会使您的代码不易出错。

【讨论】:

    猜你喜欢
    • 2017-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多