C# 奇怪的结构行为,解释一下就好了

C# 奇怪的结构行为,解释一下就好了,c#,class,struct,unity3d,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 perce

简而言之,我不明白为什么会这样:

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);
    }
}
使用UnityEngine;
使用System.Collections.Generic;
//此结构保存单个“抖动”源的数据
公共结构震动
{
公共浮动金额;
公众持股量;
公共浮动期限;
私人浮动百分比完成;
公共浮点百分比完成{get{return PercentageComplete;}}
公众震动(浮动数量、浮动数量、浮动持续时间)
{
数量x=_数量x;
数量=_数量;
持续时间=_持续时间;
完成百分比=0f;
调试日志(“wtf”);
}
公共无效更新()
{
Debug.Log(“a”+完成百分比);
percentageComplete+=Time.deltaTime;
调试日志(“b”+完成百分比);
AmountX=Mathf.Lerp(AmountX,0f,完成百分比);
AmountY=Mathf.Lerp(AmountY,0f,完成百分比);
}
}
//此类使用该数据在游戏摄影机上实现抖动
公共级摄像师:单一行为
{
//组成部分
私有变换;
//瓦尔斯
私人名单动摇;
私人浮动totalShakeX;
私人浮动完全不稳定;
私人空间
{
myTransform=转换;
shakes=新列表();
}
私有void更新()
{
if(Input.GetKeyDown(KeyCode.Space))
{
添加震动(新震动(.1f、.1f、1f));
}
totalShakeX=0f;
totalShakeY=0f;
for(int i=0;i=1f)
{
摇动。移除(i);
我--;
}
}
myTransform.position+=新矢量3(totalShakeX,0f,totalShakeY);
}
公共无效添加抖动(抖动抖动)
{
摇动。加入(摇动);
}
}
当我运行此代码(使用Unity3D游戏引擎)时,struct Shake不会记录正确的值

完成百分比记录这些值(每帧): a 0 b 0.001(或大约,其Time.deltaTime,处理前一帧所用的时间) c0

有什么好处?为什么percentageComplete每帧重置为0


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

可能是因为结构是值类型,所以将Shake对象复制到列表中会按值复制,而不是按引用复制。

可能是因为结构是值类型,所以将Shake对象复制到列表中会按值复制,而不是按引用复制。

您使用的是[邪恶易变结构]http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil)


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

您使用的是[evil mutable struct]http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil)

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

C#不允许属性访问将读和写结合起来,这意味着对结构类型属性的访问将被视为只读访问。遗憾的是,C#没有提供指示结构方法是否将修改
t的方法因此,在只读结构上调用修改此
的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]);
when
shakes
是一个
列表
,但会允许像
var temp=shakes[i];Shake.Update(ref-temp);shakes[i]=temp;
这样的构造,或者允许第一个构造(如果
shakes
Shake[]
而不是
列表,它就会起作用)

值类型与引用类型具有不同的语义。有时,这些不同的语义可能很有用。允许方便的分段变异的值类型的语义差异比不允许的值类型的语义差异更明显,一些认为所有类型的行为都应该相同的人因此谴责它们是邪恶的,但是我不同意这种想法。一个结构,如果它公开了它的所有字段,并且没有任何方法可以改变
this
,那么它的行为将与符合该描述的所有其他结构一样(除了字段的精确名称和类型)。这种行为不同于任何引用类型,但这正是它有用的原因。相比之下,带有私有字段的结构可能被编码为“假装”为了保持不变,并消除值类型的语义优势,非平凡结构类型的可变存储位置将始终保持可变结构实例,而不管这些类型是否假装不可变。总之,值类型最好以其本身的形式宣传自己,而不是假装其行为'不是。

C#不允许属性访问将读和写结合起来,这意味着对结构类型属性的访问将被视为只读访问。不幸的是,C#无法指示结构方法是否会修改此
。因此,尝试调用修改
这个
在只读结构上可以很好地编译,但是它