在Lua中,由于对象通常是作为哈希表实现的(比如JS),我可以使用以下签名编写一个补间函数(或者更确切地说,它已经被编写):
timer.tween(delay_in_s, object, table_of_target_values, algorithm)
和timer.tween
将能够访问object
中枚举的table_of_target_values
个字段以保存其初始值。然后,timer.update
也可以引用它们并设置它们的值。
如果它是值类型,有没有办法在C#中保存对类字段的引用?我知道你不能通过引用传递属性(虽然你可以在VB.NET中),但字段是可以的。但是如何在不使用反射的情况下将其保存以供其他方法以后使用?
一种解决方法是传递一个setter闭包而不是对象本身,因此签名如下所示:
Timer.Tween<T>(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm)
并将其称为:
Timer.Tween(delay, obj.fld, targetValue, (x)=>obj.fld=x, Algorithm.Linear);
这是唯一的选择吗?
编辑:为了澄清我的情况,Timer.Update(double delta)
更新了Timer.Tween()
来电中引用的所有变量,直到达到目标价值,这不仅仅是传递的问题对Timer.Tween()
的引用。
答案 0 :(得分:2)
您可以使用ref
关键字在C#中使用引用来电:
public void Swap(ref int a, ref int b)
{
int c = b;
b = a;
a = c;
}
public void DoSwap()
{
int x = 1;
int y = 2;
Console.WriteLine(x + " " + y); // should write 1 2
Swap(ref x, ref y);
Console.WriteLine(x + " " + y); // should write 2 1
}
现在,仅仅因为你能做到这一点并不意味着你应该这样做。通常情况下,你应该尝试适应语言的常用习语,而不是通过ref
来接受每种方法。
答案 1 :(得分:0)
我已经决定采用我的封闭方法并且它看起来并不那么糟糕,我甚至说它看起来很优雅。
/// <summary>
/// A static class that manages all active tweens.
/// </summary>
public abstract class Tween
{
private static HashSet<Tween> tweens = new HashSet<Tween>();
private Tween() {}
public static void Add<T>(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm, Action onCompletion)
{
var t = new TweenImpl<T>(delay, initialValue, targetValue, setter, algorithm, onCompletion);
tweens.Add(t);
}
public static void Update(double delta)
{
var toRemove = new List<Tween>();
foreach (var t in tweens) {
if (t.UpdateTween(delta)) {
toRemove.Add(t);
}
}
foreach (var t in toRemove) {
tweens.Remove(t);
}
}
internal abstract bool UpdateTween(double delta);
/// <summary>
/// A class that represents tweens.
/// </summary>
private class TweenImpl<T> : Tween
{
private double delay;
private double accumulatedDelta;
private T initialValue;
private T valueRange;
private Action<T> setter;
private Algorithm algorithm;
private Action onCompletion;
internal TweenImpl(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm, Action onCompletion)
{
this.delay = delay;
this.initialValue = initialValue;
this.valueRange = Operator.Subtract(targetValue, initialValue);
this.setter = setter;
this.algorithm = algorithm;
this.onCompletion = onCompletion;
this.accumulatedDelta = 0.0;
this.setter.Invoke(this.initialValue);
}
internal override bool UpdateTween(double delta) {
var toRemove = false;
this.accumulatedDelta += delta;
if (this.accumulatedDelta >= this.delay){
this.accumulatedDelta = this.delay;
toRemove = true;
}
var percentage = (double)this.accumulatedDelta/this.delay;
percentage = this.algorithm.Adjust(percentage);
var result = Operator.AddAlternative(this.initialValue, Operator.MultiplyAlternative(percentage, this.valueRange));
this.setter.Invoke(result);
if (toRemove) {
this.onCompletion.Invoke();
}
return toRemove;
}
}
}
我遇到的唯一问题是你不能将T限制为数字类型,因此我使用MiscUtil by John Skeet在运行时生成lambdas。当然,这意味着如果有人试图补间字符串,程序将崩溃,但这是我愿意接受的。
你这样称呼它(我在进度条上测试过):
Tween.Add(15.0, prgTest.Minimum, prgTest.Maximum, x=>prgTest.Value=x, Algorithm.Linear, ()=>{
running = false;
MessageBox.Show("Done");
});
然后只需致电
Tween.Update(delta);
位于更新循环的顶部,以自动补间变量。