c#generic与类型有细微差别?
注意两个扩展,一个用于float,一个用于Vector3。
注意var(
只有一点点差异var(
调用。
在c#中,这些可以写成一个通用的吗?
我的问题的实质是:
在一般内容中,你可以分析这种类型的性质吗?
public static IEnumerator Tweeng( this float duration, System.Action vary, float aa, float zz ) { float sT = Time.time; float eT = sT + duration; while (Time.time < eT) { float t = (Time.time-sT)/duration; vary( Mathf.SmoothStep(aa,zz, t) ); // slight difference here yield return null; } vary(zz); } public static IEnumerator Tweeng( this float duration, System.Action vary, Vector3 aa, Vector3 zz ) { float sT = Time.time; float eT = sT + duration; while (Time.time < eT) { float t = (Time.time-sT)/duration; vary( Vector3.Lerp(aa,zz, t) ); // slight difference here yield return null; } vary(zz); }
(BTW对于任何c#gurus阅读,代码示例都在Unity中,您可以在协程中访问框架系统。)
对于任何Unity开发者阅读,您如何调用Tweeng的示例
// tweeng z to 20 degrees in .12 seconds StartCoroutine(.12f.Tweeng( (t)=>transform.Eulers(0f,0f,t), 0f,20f) ); // fade in alpha in .75 seconds StartCoroutine(.75f.Tweeng( (u)=>{ca=u;s.color=c;}, 0f,1f) );
(如果您是Unity的新手并且不熟悉扩展的基本概念,请参阅介绍 。)
如果你在调用var
动作之前进行额外的Func
执行转换(你应该重命名,因为var
是一个C#关键字),你可以这样做。
您可以采取以下一种方法:
public static IEnumerator Tweeng( this float duration , System.Action varAction , T aa , T zz ) { Func transform = MakeTransform (); float sT = Time.time; float eT = sT + duration; while (Time.time < eT) { float t = (Time.time-sT)/duration; varAction(transform(aa, zz, t)); yield return null; } varAction(zz); } private static Func MakeTransform () { if (typeof(T) == typeof(float)) { Func f = Mathf.SmoothStep; return (Func)(Delegate)f; } if (typeof(T) == typeof(Vector3)) { Func f = Vector3.Lerp; return (Func)(Delegate)f; } throw new ArgumentException("Unexpected type "+typeof(T)); }
它甚至可以内联完成:
public static IEnumerator DasTweeng( this float duration, System.Action vary, T aa, T zz ) { float sT = Time.time; float eT = sT + duration; Func step; if (typeof(T) == typeof(float)) step = (Func)(Delegate)(Func)Mathf.SmoothStep; else if (typeof(T) == typeof(Vector3)) step = (Func)(Delegate)(Func)Vector3.Lerp; else throw new ArgumentException("Unexpected type "+typeof(T)); while (Time.time < eT) { float t = (Time.time-sT)/duration; vary( step(aa,zz, t) ); yield return null; } vary(zz); }
也许更自然的习语是
Delegate d; if (typeof(T) == typeof(float)) d = (Func)Mathf.SmoothStep; else if (typeof(T) == typeof(Vector3)) d = (Func)Vector3.Lerp; else throw new ArgumentException("Unexpected type "+typeof(T)); Func step = (Func)d;
您可以按如下方式定义方法:
public static IEnumerator Tweeng(this float duration, System.Action var, T aa, T zz, Func thing) { float sT = Time.time; float eT = sT + duration; while (Time.time < eT) { float t = (Time.time - sT) / duration; var(thing(aa, zz, t)); yield return null; } var(zz); }
然后使用它:
float a = 5; float b = 0; float c = 0; a.Tweeng(q => {}, b, c, Mathf.SmoothStep);
要么:
float a = 0; Vector3 b = null; Vector3 c = null; a.Tweeng(q => {}, b, c, Vector3.Lerp);
或者,如果你想摆脱方法传递,你可以有简单的重载来处理它:
public static IEnumerator Tweeng(this float duration, System.Action var, float aa, float zz) { return Tweeng(duration, var, aa, zz, Mathf.SmoothStep); } public static IEnumerator Tweeng(this float duration, System.Action var, Vector3 aa, Vector3 zz) { return Tweeng(duration, var, aa, zz, Vector3.Lerp); } private static IEnumerator Tweeng(this float duration, System.Action var, T aa, T zz, Func thing) { float sT = Time.time; float eT = sT + duration; while (Time.time < eT) { float t = (Time.time - sT) / duration; var(thing(aa, zz, t)); yield return null; } var(zz); }
然后使用它:
float a = 5; float b = 0; float c = 0; a.Tweeng(q => {}, b, c);
要么:
float a = 0; Vector3 b = null; Vector3 c = null; a.Tweeng(q => {}, b, c);
Stub方法在LINQPad中编译/没有统一:
public class Mathf { public static float SmoothStep(float aa, float zz, float t) => 0; } public class Time { public static float time => DateTime.Now.Ticks; } public class Vector3 { public static Vector3 Lerp(Vector3 aa, Vector3 zz, float t) => null; }
我喜欢Tweeng的东西,但是如果Coroutine只能用于MonoBehaviours,为什么要扩展浮点数呢? 你应该为MonoBehaviour做扩展,例如我做了一个扩展来做插值:
public static void _Interpolate(this MonoBehaviour monoBehaviour, float duration, Action callback, float from, float to, Interpolator interpolator) { monoBehaviour.StartCoroutine(ExecuteInterpolation(interpolator, duration, callback, from, to)); }
所以我刚刚在扩展程序中启动了一个Coroutine:
private static IEnumerator ExecuteInterpolation(Interpolator interpolator, float duration, Action callback, float from, float to) { float sT = Time.time; float eT = sT + duration; bool hasFinished = false; while (Time.time < eT) { float t = (Time.time - sT) / duration; // ----> my logic here with callback(to, false) yield return null; } hasFinished = true; callback(to, hasFinished); }
请注意,我有一个布尔值表示插值已完成,这是因为依靠浮点比较来检查流的结束不是最佳实践,如果它为最大结果舍入结果在最后一个之前我们将要具有回调最后一次调用两次。