具有Struct内部值的意外行为

本文关键字:意外 Struct 内部 具有 | 更新日期: 2024-10-21 01:06:38

*已解决。谢谢你们的解释,伙计们,我没有完全理解在这种情况下使用值类型的含义。

我有一个从静态类中使用的结构。然而,当我在运行时打印它的内部状态时,该行为显示出意外的行为。这是我的结构:

    public struct VersionedObject
    {
        public VersionedObject(object o)
        {
            m_SelectedVer = 0;
            ObjectVersions = new List<object>();
            ObjectVersions.Add(o);
        }
        private int m_SelectedVer;
        public int SelectedVersion
        {
            get
            {
                return m_SelectedVer;
            }
        }
        public List<object> ObjectVersions;//Clarifying:  This is only used to retrieve values,  nothing is .Added from outside this struct in my code.
        public void AddObject(object m)
        {
            ObjectVersions.Add(m);
            m_SelectedVer = ObjectVersions.Count - 1;
        }
    }

测试代码

        VersionedObject vo = new VersionedObject(1);
        vo.AddObject(2);//This is the second call to AddObject()
        //Expected value of vo.SelectedVerion:  1
        //Actual value of vo.SelectedVersion:   1

现在,如果您单独测试此代码,即将其复制到您的项目中进行测试,将返回预期结果

问题我在生产代码中观察到的是以下调试输出:

objectName, ObjectVersions.Count:2, SelectedVer:0,

为什么?根据我的理解和测试,这在任何情况下都是完全不可能的。

我的随机猜测是,存在某种不变性,出于某种原因,新结构正在通过默认构造函数实例化,ObjectVersions数据正在复制,但m_SelectedVersion是私有的,无法复制到新结构中
我使用静态类和方法来操作结构与此有关吗?

我被难住了,在这一点上我只是胡乱猜测。

具有Struct内部值的意外行为

Struct是值类型。因此,很可能您正在实际代码中创建对象的多个副本。

考虑简单地将结构更改为类,因为结构的内容实际上不适合值类型(因为它是可变的,并且还包含可变的引用类型)。

关于"结构是值类型"的更多信息:

首先查看常见问题解答,其中已经有许多好的答案。

值类型是通过值传递的,所以若您调用函数来更新这样的对象,它将不会更新原始对象。您可以像将整数值传递给函数一样对待它们:即,您希望SomeFunction(42)能够更改42的值吗?

struct MyStruct { public int V;}
void UpdateStruct(MyStruct x)
{
  x.V = 42; // updates copy of passed in object, changes will not be visible outside.
}
....
var local = new MyStruct{V = 13}
UpdateStruct(local); // Hope to get local.V == 42
if (local.V == 13) {
  // Expected. copy inside UpdateStruct updated,
  // but this "local" is untouched.
}

为什么这是一个结构而不是一个类?更好的是,为什么要跟踪后备存储(List<T>)的大小,而不是让List<T>为您跟踪。由于底层后台存储是公共的,因此可以在结构不知情的情况下对其进行操作。我怀疑您的生产代码中的某些内容在没有经过您的结构的情况下添加到了后台存储中。

如果是我,我会把它设置成这样,尽管我会让它成为一门课。。。但这几乎可以肯定是一个突破性的变化:

public struct VersionedObject
{
    public VersionedObject()
    {
        this.ObjectVersions = new List<object>() ;
        return ;
    }
    public VersionedObject(object o) : this()
    {
        ObjectVersions.Add(o);
        return ;
    }
    public VersionedObject( params object[] o ) : this()
    {
        ObjectVersions.AddRange( o ) ;
        return ;
    }
    public int SelectedVersion
    {
        get
        {
            int value = this.ObjectVersions.Count - 1 ;
            return value ;
        }
    }
    public List<object> ObjectVersions  ;
    public void AddObject(object m)
    {
        ObjectVersions.Add(m);
        return ;
    }
}

您会注意到,这与您的结构具有相同的语义,但SelectedVersion属性现在反映了后台存储中的实际内容。