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

时间:2015-10-30 11:39:10

标签: c# unity3d compiler-errors coroutine ienumerator

我使用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类下载了一些东西,当我通过www.progress作为参考时,我也收到了这个错误:
A property or indexer 'UnityEngine.WWW.progress' may not be passed as 'ref' or 'out' parameter

2 个答案:

答案 0 :(得分:1)

从函数参数中删除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参数传递给交互器。

答案 1 :(得分:0)

考虑一下

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取消大量编码而为您执行此操作)但无法正常工作。

你需要以某种方式对float进行封装,无论你是保持对同一个float的object引用,还是迭代器可以更新引用类型,或者类似的类型框:

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

这又允许您通过引用类型访问该值,但不需要转换成本。

更常见的是,根据传入的任何值迭代值会更有用。