VB.NET中“yield return”的等价语法是什么?

时间:2010-05-26 12:23:31

标签: c# vb.net loops yield

使用下面的C#代码,您将如何在Visual Basic中编写它?它试图说什么?

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Microsoft.LiveLabs.Pivot
{
    /// <summary>
    ///     Tile Builder class
    /// </summary>
    public static class TileBuilder
    {
        /// <summary>
        ///     Specifies which images are required in the images array used in CreateTile
        ///     according to the Morton fractal pattern used by Seadragon.
        /// </summary>
        /// <remarks>
        ///     Usage of this and CreateTile are kind of tricky. Here's an example:
        ///     Say you have a results set that is a collection of items like so: { item1, item2, ..., item100 }
        ///     Since Seadragon expects the tiles to be laid out in a Morton pattern,
        ///     level 6 will look like the following:
        ///
        ///     --------------------------
        ///     |0   1   4   5  | 16  17...
        ///     |2   3   6   7  | 18  19
        ///     |8   9   12  13 | 24  25
        ///     |10  11  14  15 | 26  27
        ///     |-----------------------
        ///     |32  33  36  37 | 48  49
        ///     |34  35  38  39 | 50  51...
        ///     |.          .
        ///     |.          .
        ///      .          .
        ///
        ///      Each tile at level 6 is 4x4, so the dashes represent tile boundaries. Now, say
        ///      you want to build 0,0. You need the images on that tile. The ids 0, 1, 4, 5...
        ///      represent the ids in your result set, { item1, item2, ..., item100 }. Calling
        ///      this method tells you the ids to use for a given tile. You then must retrieve
        ///      the necessary images out the result set, and supply them in the order this
        ///      method gave you to CreateTile. This will result in a correctly built tile
        ///      that Seadragon can use.
        /// </remarks>
        /// <param name="imageCount">Number of images in the full set.</param>
        /// <param name="level">The level to which each image will be downsampled.</param>
        /// <param name="row">The row number which specifies what images to render.</param>
        /// <param name="column">The row number which specifies what images to render.</param>
        /// <param name="tileSize">The size of the tile to return.</param>
        public static IEnumerable<int> GetTileIds(
          int imageCount,
          int level,
          int row,
          int column,
          int tileSize)
        {
            // Calculate upper-left hand corner of tile in image space (1 unit = 1 image)
            int levelSize = (int)Math.Pow(2, level);
            int imagePerSide = tileSize / levelSize;
            int xOffset = row * imagePerSide;
            int yOffset = column * imagePerSide;

            if (imagePerSide <= 0)
            {
                throw new ArgumentOutOfRangeException("Level is greater than the maximum depth allowed by the tile size, or the tile size is 0.");
            }

            // Loop through x and y in image space, starting at the upper-left
            // hand corner of the tile. Find all ids on the given tile.
            for (int x = 0; x < imagePerSide; x++)
            {
                for (int y = 0; y < imagePerSide; y++)
                {
                    int n = XYToMorton(x + xOffset, y + yOffset);
                    if (n < imageCount)
                    {
                        yield return n;
                    }
                }
            }
        }

        /// <summary>
        ///     Create a tile for a collection according to the Morton fractal
        ///     pattern used by Seadragon.
        /// </summary>
        /// <remarks>
        ///     See GetTileIds for more information.
        /// </remarks>
        /// <param name="imageCount">The total number of images in the collection.</param>
        /// <param name="images">Jpeg images to render on this tile.
        ///     If this is null, a blank tile will be returned.
        ///     See GetTileIds remarks for more information.</param>
        /// <param name="level">The level to which each image will be downsampled.</param>
        /// <param name="row">The row number which specifies what images to render.</param>
        /// <param name="column">The row number which specifies what images to render.</param>
        /// <param name="tileSize">The size of the tile to return.</param>
        /// <param name="output">The stream to use to output the result.</param>
        public static void CreateTile(
          int imageCount,
          IEnumerable<ImageBag> images,
          int level,
          int row,
          int column,
          int tileSize,
          string fileType,
          Stream output)
        {
            // Calculate upper-left hand corner of tile in image space (1 unit = 1 image).
            int levelSize = (int)Math.Pow(2, level);
            int imagePerSide = tileSize / levelSize;
            int xOffset = row * imagePerSide;
            int yOffset = column * imagePerSide;

            if (imagePerSide <= 0)
            {
                throw new ArgumentOutOfRangeException("Level is greater than the maximum depth allowed by the tile size, or the tile size is 0.");
            }

            if (output == null)
            {
                throw new ArgumentNullException("The given output stream is null.");
            }

            // Create the tile.
            WriteableBitmap outputBitmap = new WriteableBitmap(
              tileSize,
              tileSize,
              96,
              96,
              PixelFormats.Bgr24,
              null);

            // If images is null, return a blank tile.
            if (images != null)
            {
                // Loop through the tile in relative x and y image-space.
                IEnumerator<ImageBag> imageEnumerator = images.GetEnumerator();
                for (int x = 0; x < imagePerSide; x++)
                {
                    for (int y = 0; y < imagePerSide; y++)
                    {
                        // Convert to Morton id-space from the absolute image-space (to get absolute, add offsets).
                        int n = XYToMorton(x + xOffset, y + yOffset);
                        if (n < imageCount)
                        {
                            if (imageEnumerator.MoveNext())
                            {
                                if (imageEnumerator.Current == null)
                                {
                                    continue;
                                }

                                // Compute the pixel location
                                int locX = levelSize * x;
                                int locY = levelSize * y;

                                int width = 0;
                                int height = 0;
                                imageEnumerator.Current.ImageSize(out width, out height);
                                MemoryStream imageStream = new MemoryStream(imageEnumerator.Current.ImageData);

                                // Determine the largest tile size to the nearest power of two for
                                // this image: 2^ceil(lg max(width, height)).
                                double maxTileSize = Math.Pow(2, Math.Ceiling(Math.Log(Math.Max(width, height), 2)));

                                // Downsample to the correct size and decompress the image. The correct size
                                // is total dimenion of the image * level size / max tile size required for
                                // total width. Think of this as dividing the dimensions by two foreach
                                // levels, starting at the max tile size, and going up to the current
                                // tile size
                                TransformedBitmap downsampledImage = JpegDecoder.DownsampleJpeg(
                                    imageStream,
                                    Math.Ceiling(width * levelSize / maxTileSize),
                                    Math.Ceiling(height * levelSize / maxTileSize));

                                // Copy the pixels to a buffer and then write them to the
                                // appropriate region on the output image.
                                int stride = (downsampledImage.PixelWidth * downsampledImage.Format.BitsPerPixel + 7) / 8;
                                byte[] buffer = new byte[stride * downsampledImage.PixelHeight];
                                downsampledImage.CopyPixels(buffer, stride, 0);

                                Int32Rect outputRect = new Int32Rect(locX, locY, downsampledImage.PixelWidth, downsampledImage.PixelHeight);
                                outputBitmap.WritePixels(outputRect, buffer, stride, 0);
                            }
                            else
                            {
                                // We should render the image, but we're done with our list.
                                // So, exit both loops.
                                x = imagePerSide;
                                y = imagePerSide;
                            }
                        }
                        else
                        {
                            // Since n is monotonic wrt y, we know y has gone too far down, so
                            // we can reset it.
                            y = imagePerSide;
                        }
                    }
                }
            }

            // Write the output
            BitmapFrame outputFrame = BitmapFrame.Create(outputBitmap);
            BitmapEncoder encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(outputFrame);
            encoder.Save(output);
        }

        /// <summary>
        ///     Converts an x and y to a Morton number
        /// </summary>
        /// <param name="x">x location to convert.</param>
        /// <param name="y">y location to convert.</param>
        /// <returns>Returns the morton number which corresponds to the
        ///          given x and y coordinates.</returns>
        private static int XYToMorton(int x, int y)
        {
            const uint BITS_PER_BYTE = 8;
            const uint BIT_PAIRS = sizeof(int) * BITS_PER_BYTE / 2;

            int morton = 0;
            for (int i = 0; i < BIT_PAIRS; i++)
            {
                morton |= (x & 1) << (i * 2);
                morton |= (y & 1) << ((i * 2) + 1);
                x >>= 1;
                y >>= 1;
            }

            return morton;
        }
    }
}

6 个答案:

答案 0 :(得分:16)

没有。期。除非您要编写自己的状态机,否则没有快速解决方法。请参阅博客文章 VB.NET's "yield return"

对于那些关心实际生成内容的人(是的,我喜欢C#预编译器和编译器:)):

尝试编译并使用.NET Reflector或其他内容查看生成的代码:

class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foreach(var result in foo.Bar())
        {
            Console.WriteLine(result);
        }

        Console.ReadLine();
    }
}

class Foo
{
    public IEnumerable<char> Bar()
    {
        const char start = 'a';

        for(int x = 0;x < 26;x++)
        {
            yield return (char)(start + x);
        }
    }
}

我不会复制结果,这是巨大的。但是看一看,你会发现解决起来并不简单。

答案 1 :(得分:3)

VB中没有yield的完全重复。您最好的选择是创建正在yield的集合并将整个集合返回给调用代码进行迭代。这与您给出的代码并不完全相同,但它应该具有相同的整体效果。 yield基本上只是语法糖,用于一次一个地将一个集合的部分返回到foreach循环,以优化运行时间并允许早期突破(如果您决定不需要枚举所有内容)毕竟)。

答案 2 :(得分:3)

包含yield return / yield break语句的代码块称为iterator。 VB中不存在此功能。当然,可以“翻译”该代码,但它不是非常简单......您需要创建一个实现IEnumerator(Of Integer)的自定义类来重现完全相同的行为:

  • 使用原始函数的参数
  • 对其进行初始化
  • 通过将循环变量存储在私有字段中,在MoveNext方法中实现“循环”逻辑。此方法还应设置一个字段以保存当前值(原始函数中的n
  • Current属性
  • 返回当前值
  • 重置Reset方法
  • 中的循环变量
  • Dispose方法
  • 中不执行任何操作

这是可行的,但老实说,这是一种痛苦......

另一种更简单的选择是使用结果填充List(Of Integer)并返回该列表:只需将yield return n替换为list.Add(n)

答案 3 :(得分:1)

在VB.NET中没有等同于yield return,但它可以被模拟。

请参阅Stack Overflow问题 Yield in VB.NET 及其答案。

也许Visual Studio Magazine文章 Use Iterators in VB Now 会有所帮助。

答案 4 :(得分:1)

没有。但是,您可以创建一个 创建一个迭代器类 这样做的几个步骤:

  1. 创建一个基本上是迭代步骤的函数。该函数应该采用每个计算所需的任何参数,并返回参数以进行下一次计算。 (为了实现yield break以及yield return,这个函数也需要能够返回一些标记结束的布尔值)
  2. 创建一个实现IEnumerator的类。 Current属性应返回最新的计算值,Reset应将所有值重置为其初始值,MoveNext函数应计算下一个值,如果计算函数返回false,则返回false本身。
  3. 要获得额外的功劳,请使该类具有通用性,并且能够使用委托给予它的任何功能 你可以简化这一点并使MoveNext成为计算函数本身,但这样就可以很容易地从同一个类实现中创建不同的迭代器。

答案 5 :(得分:-11)

C#:

yield return scriptRef

VB.NET:

Return New List(Of ScriptReference) From {scriptRef}