c#generic与类型略有不同?

时间:2016-03-20 23:55:16

标签: c# generics

注意两个扩展,一个用于float,一个用于Vector3。

请注意,var(电话只有轻微差异。

在c#中,这些可以写成一个通用的吗?

我的问题的实质是:

在通用的

中,您能否分析该类型的性质?

public static IEnumerator Tweeng( this float duration,
         System.Action<float> 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<Vector3> 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)=>{c.a=u;s.color=c;}, 0f,1f) );

(如果您是Unity的新手并且不熟悉扩展的基本概念,那么这里是intro。)

3 个答案:

答案 0 :(得分:3)

如果您在调用Func<T,T>操作之前再进行var执行转换,则可以执行此操作(您应该重命名,因为var是C#关键字)。

您可以采取以下一种方法:

public static IEnumerator Tweeng<T>(
    this float duration
,   System.Action<T> varAction
,   T aa
,   T zz
) {
    Func<T,T,float,T> transform = MakeTransform<T>();
    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<T,T,float,T> MakeTransform<T>() {
    if (typeof(T) == typeof(float)) {
        Func<float, float, float, float> f = Mathf.SmoothStep;
        return (Func<T,T,float,T>)(Delegate)f;
    }
    if (typeof(T) == typeof(Vector3)) {
        Func<Vector3, Vector3, float, Vector3> f = Vector3.Lerp;
        return (Func<T,T,float,T>)(Delegate)f;
    }
    throw new ArgumentException("Unexpected type "+typeof(T));
}

它甚至可以内联:

public static IEnumerator DasTweeng<T>( this float duration, System.Action<T> vary, T aa, T zz )
    {
    float sT = Time.time;
    float eT = sT + duration;

    Func<T,T,float,T> step;

    if (typeof(T) == typeof(float))
        step = (Func<T,T,float,T>)(Delegate)(Func<float, float, float, float>)Mathf.SmoothStep;
    else if (typeof(T) == typeof(Vector3))
        step = (Func<T,T,float,T>)(Delegate)(Func<Vector3, Vector3, float, Vector3>)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<float, float, float, float>)Mathf.SmoothStep;
    else if (typeof(T) == typeof(Vector3))
        d = (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp;
    else
        throw new ArgumentException("Unexpected type "+typeof(T));

    Func<T,T,float,T> step = (Func<T,T,float,T>)d;

答案 1 :(得分:3)

您可以按如下方式定义方法:

public static IEnumerator Tweeng<T>(this float duration,
         System.Action<T> var, T aa, T zz, Func<T,T,float,T> 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<float> var, float aa, float zz)
{
    return Tweeng(duration, var, aa, zz, Mathf.SmoothStep);
}
public static IEnumerator Tweeng(this float duration, System.Action<Vector3> var, Vector3 aa, Vector3 zz)
{
    return Tweeng(duration, var, aa, zz, Vector3.Lerp);
}

private static IEnumerator Tweeng<T>(this float duration,
         System.Action<T> var, T aa, T zz, Func<T,T,float,T> 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; }

答案 2 :(得分:0)

我喜欢Tweeng的东西,但是如果Coroutine只能用于MonoBehaviours,为什么要扩展float呢?你应该为MonoBehaviour做扩展,例如我做了一个扩展来做插值:

public static void _Interpolate(this MonoBehaviour monoBehaviour, float duration,
    Action<float, bool> 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<float, bool> 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);
}

请注意,我有一个布尔值表示插值已完成,这是因为依靠浮点比较来检查流的结束不是最佳做法,如果它在最后一个结果之前将结果舍入到最后的结果为最后一次调用两次调用回调。