奇怪的结构行为,解释会很棒

时间:2012-10-23 18:18:31

标签: c# class struct unity3d

简短而简单,我只是不明白为什么它的工作方式如下:

using UnityEngine;
using System.Collections.Generic;

// this struct holds data for a single 'shake' source
public struct Shake
{
    public float AmountX;
    public float AmountY;
    public float Duration;

    private float percentageComplete;
    public float PercentageComplete { get { return percentageComplete; } }

    public Shake(float _amountX, float _amountY, float _duration)
    {
        AmountX = _amountX;
        AmountY = _amountY;
        Duration = _duration;
        percentageComplete = 0f;
        Debug.Log("wtf");
    }

    public void Update()
    {
        Debug.Log("a " + percentageComplete);
        percentageComplete += Time.deltaTime;
        Debug.Log("b " + percentageComplete);

        AmountX = Mathf.Lerp(AmountX, 0f, percentageComplete);
        AmountY = Mathf.Lerp(AmountY, 0f, percentageComplete);

    }
}

// This class uses that data to implement the shakes on a game camera
public class CameraShake : MonoBehaviour 
{
    // components
    private Transform myTransform;

    // vars
    private List<Shake> shakes;

    private float totalShakeX;
    private float totalShakeY;

    private void Awake()
    {
        myTransform = transform;

        shakes = new List<Shake>();
    }

    private void Update() 
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            AddShake(new Shake(.1f, .1f, 1f));
        }

        totalShakeX = 0f;
        totalShakeY = 0f;

        for (int i = 0; i < shakes.Count; i++)
        {
            shakes[i].Update();
            Debug.Log("c " + shakes[i].PercentageComplete);

            totalShakeX += shakes[i].AmountX;
            totalShakeY += shakes[i].AmountY;

            if (shakes[i].PercentageComplete >= 1f)
            {
                shakes.RemoveAt(i);
                i--;
            }
        }

        myTransform.position += new Vector3(totalShakeX, 0f, totalShakeY);
    }

    public void AddShake(Shake shake)
    {
        shakes.Add(shake);
    }
}

当我运行此代码(使用Unity3D游戏引擎)时,struct Shake不会记录正确的值。

百分比完成记录这些值(每帧): 一个0 b 0.001(或以下,其Time.deltaTime,处理前一帧所花费的时间) c 0

是什么给出的?为什么百分比完成每帧重置为0?

如果我将Shake更改为类而不是结构,它可以正常工作。

3 个答案:

答案 0 :(得分:1)

也许是因为结构是值类型,因此将Shake对象复制到列表中会按值复制,而不是通过引用复制。

答案 1 :(得分:1)

你正在使用[邪恶的可变结构] http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil)。

当您从列表(或来自强类型字段或局部变量之外的任何其他)调用结构上的方法时,它将在结构的新副本上调用该方法,而不会影响原始结构。

答案 2 :(得分:1)

C#不允许属性访问组合读取和写入,这意味着对结构类型的属性的访问将被视为只读访问。不幸的是,C#没有提供指示struct方法是否会修改this的方法。因此,尝试调用在只读结构上修改this的struct方法将编译正常,但它实际上不起作用。

为了避免这个问题,如果你的struct需要定义一个像Update这样的方法来修改一个结构,你应该将它改为:

public static void Update(ref Shake it)
{
    it.percentageComplete += it.Time.deltaTime;
    it.AmountX = Mathf.Lerp(it.AmountX, 0f, it.percentageComplete);
    it.AmountY = Mathf.Lerp(it.AmountY, 0f, it.percentageComplete);
}

如果你在Shake.Update(ref shakes[i]);shakes时尝试类似List<Shake>的话,编译器就能正确地发出尖叫声,但是会允许{{1}这样的构造如果var temp = shakes[i]; Shake.Update(ref temp); shakes[i] = temp;shakes而不是Shake[],则会允许第一个构造(并且它会起作用)。

值类型与引用类型具有不同的语义。有时这些不同的语义可能很有用。对于那些允许方便的分段变异的价值类型而言,语义上的差异更明显,而那些相信所有类型的人应该表现得同样谴责它们是邪恶的,但我不同意这种看法。暴露其所有字段并且没有变异List<Shake>的方法的结构体将像满足该描述的每个其他结构一样(除了其字段的精确名称和类型)。这种行为与任何引用类型不同,但这就是使它有用的原因。相比之下,虽然可以对具有私有字段的结构进行编码以“假装”为不可变的,并消除值类型的语义优势,但是非平凡结构类型的可变存储位置将始终包含可变结构实例,无论类型是否假装是不可变的。恕我直言,价值类型最好将自己宣传为自己的东西,而不是假装表现为他们不是的东西。