0

To clarify I'm doing this in Unity3D, which may or may not be important?

I'm trying to figure out if I can pass a value by ref to an IEnumerator function that does not yield. If I try to do it with one that yields, VS2010 complains ("Iterators cannot have ref or out parameters"), but, if I wrap the call up with a similar IEnumerator function that calls the yielding function, but does not yield itself, the error goes away and things appear to work. I'm trying to find out if I'm in unexpected behavior land or if this is normal behavior.

Here's an example of what I'm doing:

IEnumerator Wrapper(ref int value)
{
    int tmp = ++value;    // This is the ONLY place I want the value
    return Foo(tmp);      // of the ref parameter to change!  
}                         // I do _NOT_ want the value of the ref
                          // parameter to change in Foo()!
IENumerator Foo(int value)
{
    // blah blah
    someFunc(value);
    someSlowFunc();
    yield return null;
    yield return null;
}
4

1 回答 1

1

看起来不错。顶部函数只返回一个 IEnumerator - 但在其他方面是一个普通函数。底部函数一个 IEnumerator [由编译器转换为一个时髦的类],因此不能有 ref 值。

顶层函数可以这样写:

 void Wrapper(ref int value, out IEnumerator coroutine)
 {
     int tmp = ++value;
     coroutine = Foo(tmp);
 }

这有点混乱 - 但它显示了这是一个处理两条数据的普通函数。通过引用传递的 int 和返回的 IEnumerator [只是一个类] [在本示例中使用 out]。


补充:这就是幕后工作的方式:

    static void Main(string[] args)
    {
        //Lets get the 'IEnumerable Class' that RandomNum gets compiled down into.
        var IEnumeratorClass = RandomNum(10, 10);

        //All an IEnumerable is is a class with 'GetEnumerator'... so lets get it!
        var IEnumerableClass = IEnumeratorClass.GetEnumerator();

        //It can be used like so:
        while (IEnumerableClass.MoveNext())
        {
            Console.WriteLine(IEnumerableClass.Current);
        }

        Console.WriteLine(new String('-', 10));

        //Of course, that's a lot of code for a simple job.
        //Luckily - there's some nice built in functionality to make use of this.
        //This is the same as above, but much shorter
        foreach (var random in RandomNum(10, 10)) Console.WriteLine(random);

        Console.WriteLine(new String('-', 10));

        //These simple concepts are behind Unity3D coroutines, and Linq [which uses chaining extensively]
        Enumerable.Range(0, 100).Where(x => x % 2 == 0).Take(5).ToList().ForEach(Console.WriteLine);

        Console.ReadLine();
    }

    static Random rnd = new Random();
    static IEnumerable<int> RandomNum(int max, int count)
    {
        for (int i = 0; i < count; i++) yield return rnd.Next(i);
    }

    //This is an example of what the compiler generates for RandomNum, see how boring it is?
    public class RandomNumIEnumerableCompiled : IEnumerable<int>
    {
        int max, count;
        Random _rnd;
        public RandomNumIEnumerableCompiled(int max, int count)
        {
            this.max = max;
            this.count = count;
            _rnd = rnd;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new RandomNumIEnumeratorCompiled(max, count, rnd);
        }

        IEnumerator<int> IEnumerable<int>.GetEnumerator()
        {
            return new RandomNumIEnumeratorCompiled(max, count, rnd);
        }

    }
    public class RandomNumIEnumeratorCompiled : IEnumerator<int>
    {
        int max, count;
        Random _rnd;
        int current;
        int currentCount = 0;
        public RandomNumIEnumeratorCompiled(int max, int count, Random rnd)
        {
            this.max = max;
            this.count = count;
            _rnd = rnd;
        }

        int IEnumerator<int>.Current { get { return current; } }

        object IEnumerator.Current { get { return current; } }

        public bool MoveNext()
        {
            if (currentCount < count)
            {
                currentCount++;
                current = rnd.Next(max);
                return true;
            }
            return false;
        }

        public void Reset() { currentCount = 0; }
        public void Dispose() { }
    }
于 2012-08-01T04:52:07.590 回答