c# generic with slight difference for types?

前端 未结 3 2059
庸人自扰
庸人自扰 2020-12-19 14:16

Notice the two extensions, one for float, one for Vector3.

Notice there\'s only a slight difference in the var( call.

In c# could these be writt

相关标签:
3条回答
  • 2020-12-19 14:26

    You can do it if you make an extra Func<T,T> that performs transformation before calling the var action (which you should rename, because var is a C# keyword).

    Here is one approach that you could take:

    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));
    }
    

    It can even be done inline:

    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);
        }
    

    Perhaps a more natural idiom is

        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;
    
    0 讨论(0)
  • 2020-12-19 14:31

    You can define your method as follows:

    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);
    }
    

    And then using it:

    float a = 5;
    float b = 0;
    float c = 0;
    a.Tweeng(q => {}, b, c, Mathf.SmoothStep);
    

    Or:

    float a = 0;
    Vector3 b = null;
    Vector3 c = null;
    a.Tweeng(q => {}, b, c, Vector3.Lerp);
    

    Alternatively, if you want to get rid of the method passing, you can have simple overloads to handle it:

    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);
    }
    

    And then using it:

    float a = 5;
    float b = 0;
    float c = 0;
    a.Tweeng(q => {}, b, c);
    

    Or:

    float a = 0;
    Vector3 b = null;
    Vector3 c = null;
    a.Tweeng(q => {}, b, c);
    


    Stub methods to compile in LINQPad/without unity:

    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; }
    
    0 讨论(0)
  • 2020-12-19 14:50

    I liked the Tweeng thing, but why extend float if the Coroutine can only be used on MonoBehaviours? You should do the extensions for MonoBehaviour so, for example I did a extension to do Interpolation:

    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));
    }
    

    So I just Started a Coroutine inside the extension:

    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);
    }
    

    Note that I have a boolean to say that the interpolation finished, this happens because is not best practice to rely on float comparison to check end of a flow, if it rounds for the max result the result before the last we are going to have the call back for the last interaction called twice.

    0 讨论(0)
提交回复
热议问题