将引用(和 Unity 的 WWW.progress)传递给 Coroutines/IEnumerators

Passing references (And Unity's WWW.progress) to Coroutines/IEnumerators

我使用 Unity 5 在 C# 中创建了一个协程,我想传递对它的引用。它似乎没有用,所以我制作了一个测试脚本来尝试隔离以下错误。
error CS1623: Iterators cannot have ref or out parameters

这是测试脚本:

using UnityEngine;
using System.Collections;

public class TEMP : MonoBehaviour {

    // Use this for initialization
    void Start () {
        float var = 3.141f;
        StartCoroutine ( test (ref var) );
    }

    IEnumerator test (ref float value) {
        for (int i = 1; i <= 10; i++) {
            Debug.Log (value);
            yield return null;
        }
    }
}

此外,在我的实际脚本中,我使用Unity的WWW class下载东西,我在传递www.progress作为参考时也出现此错误:
A property or indexer 'UnityEngine.WWW.progress' may not be passed as 'ref' or 'out' parameter

从函数参数中删除 ref。

// Use this for initialization
void Start () {
    float var = 3.141f;
    StartCoroutine ( test (var) );
}

IEnumerator test (float value) {
    for (int i = 1; i <= 10; i++) {
        Debug.Log (value);
        yield return null;
    }
}

根据您的要求,您可以使用 out 参数执行如下操作。我不确定您打算如何使用这些方法,但我认为您对 ref 参数及其工作方式的理解是错误的。

class Program
{
    static void Main(string[] args)
    {
        float var = 3.14f;
        StartCoroutine(test(var), out var);

    }

    static IEnumerator test(float value)
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine(value);
            yield return value + 1;
        }
    }

    static void StartCoroutine(IEnumerator test, out float update)
    {
        update = 0;
        while (test.MoveNext())
        {
            update++;
            Console.WriteLine("Executing..." + update);
        }
    }
}

但是,您永远无法将 ref 或 out 参数传递给交互器。

考虑一下

IEnumerator test (float value)
{
  for (int i = 1; i <= 10; i++)
  {
    Debug.Log(value);
    yield return null;
  }
}

是像这样的语法糖:

private class Iterator : IEnumerable<object>, IEnumerator<object>
{
  private int _state;
  private int _threadId = Environment.CurrentManagedThreadId;
  private object _current;
  private float _value;
  int i;
  public Iterator(float value)
  {
    _value = value;
  }
  public object Current‎
  {
    get { return _current; }
  }
  object IEnumerator.Current
  {
    get { return _current; }
  }
  public IEnumerator<object> GetEnumerator()
  {
    // If we're on the same thread and its the first call, reuse this object as the enumerator
    // otherwise create a fresh one for new call (so we don't have buggy overlaps) or a different
    // thread (to avoid a race on that first call).
    Iterator iter = _state == 0 && _threadId == Environment.CurrentManagedThreadId ? this : new Iterator(_value);
    iter._state = 1;
    return iter;
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
    return GetEnumerator();
  }
  public bool MoveNext()
  {
    switch(_state)
    {
      case 1:
        i = 1;
        _state = 2;
        goto case 2;
      case 2:
        if (i <= 10)
        {
          Debug.Log(_value);
          _current = null;
          ++i;
          return true;
        }
        _state = -1;
        break;
    }
    return false;
  }
  public void Dispose()
  {
  }
  public void Reset()
  {
    throw new NotSupportedException();
  }
}
private IEnumerator test(float value)
{
  return new Iterator(value);
}

现在,请考虑您的 float 参数已成为实施中的 float 字段。由于您不能拥有 ref 字段,因此您不能将其作为看起来像 ref 参数的语法糖。

因此,您的方法不仅行不通(C# 不会为您做,yield 带走了很多编码)而且行不通。

您需要以某种方式将浮点数装箱,无论您是持有对同一个浮点数的 object 引用,作为引用类型可以由迭代器更新,还是类型化盒子像:

private class TypedBox<T> where T : struct
{
  public T Value { get; set; }
  public TypedBox(T value)
  {
    Value = value;
  }
}

这再次允许您通过引用类型访问值,但没有转换成本。

更多时候,根据传入的任何值迭代值更有用。