C#指针、迭代器和泛型

本文关键字:泛型 迭代器 指针 | 更新日期: 2023-09-27 18:23:37

我被难住了

如何在C#中像使用C++迭代器一样使用迭代器?我找不到Begin()或End()访问器,甚至找不到如何声明迭代器。我读过关于分子的书。我的目标是实现合并函数。这是我用C++编写的Merge函数的一部分。大多数情况下,我正在寻找与所示内容等效的C#,只是我将使用引用类型而不是整数。

void merge(vector<int>::iterator left, vector<int>::iterator right, vector<int>::iterator      leftEnd, vector<int>::iterator rightEnd, vector<int>::iterator full)
{
    while(left != leftEnd && right!= rightEnd) //compare left and right until the end of the vector is reached
    {
        if(*right < *left)      //right < left so insert right to the output vector and advance the iterators
        {
            *full++ = *right++;
        }
        else                   //left < right so insert left to the output vector and advance the iterators
        {
            *full++ = *left++;
        }
    }
    while(left != leftEnd)    //copy any remaining elements into the output from left 
    {
        *full++ = *left++;
    }
}

此外,我应该使用哪些集合?(目前我一直在尝试List<T>LinkedList<T>)。

C#指针、迭代器和泛型

听起来你想要这样的东西:

bool leftValid = left.MoveNext();
bool rightValid = right.MoveNext();
while (leftValid && rightValid)
{
    if (right.Current < left.Current)
    {
        full.Add(right.Current);
        rightValid = right.MoveNext();
    }
    else
    {
        full.Add(left.Current);
        leftValid = left.MoveNext();
    }
}
while (leftValid)
{
    full.Add(left.Current);
    leftValid = left.MoveNext();    
}
while (rightValid)
{
    full.Add(right.Current);
    rightValid = right.MoveNext();    
}

这里full需要是某种IList<T>-.NET迭代器不允许您对底层集合进行更改。

您不应该试图编写"桥接"代码来使用像C++那样的.NET迭代器;当您使用.NET.时,最好尝试从.NET迭代器的角度开始思考

请注意,在.NET中传递迭代器是非常罕见的。将方法设置为IEnumerable<T>参数会更自然,并执行以下操作:

using (IEnumerable<T> leftIterator = leftSequence.GetEnumerator())
{
    using (IEnumerable<T> rightIterator = rightSequence.GetEnumerator())
    {
        // Code as above, just using leftIterator and rightIterator
        // instead of left and right
    }
}

.net容器不支持C++风格的迭代器。他们只有

  • 称为IEnumerator<T>的简单前向迭代器
  • 无法修改集合
  • 不是随机访问
  • 无法复制(有些集合具有可以复制的值类型迭代器,但这是一项棘手的业务,很少使用)
  • 在大多数集合上,每当修改集合时也会失效

它们几乎唯一能做的就是在foreach语句中进行迭代。


您可能想了解IList<T>接口,它允许随机访问,但仅在支持快速索引的集合上受支持。在这样的集合上,您可以通过使用索引来实现就地合并排序。

void Merge<T>(IList<T> container,int left, int right, int leftEnd, int rightEnd, int full)

然后使用CCD_ 9而不是CCD_。


不幸的结果是,您无法实现像C++那样高效的容器无关的就地排序函数。

我认为您想要GetEnumerator()、MoveNext()和Current。

通常,您可以只使用foreach进行迭代,但您的情况很特殊。

如果事实是这样的话,与其使用"full",不如将其组织为迭代器块,并惰性地合并两个枚举。

IEnumerable<T> Merge<T>(IEnumerable<T> left, IEnumerable<T> right)
{
    ... yield return Min<T>(left.Current, right.Current); ..,
}

您可以使用具有固定大小的数组,也可以使用List<T>,在其他语言中也称为ArrayLists。它们的项目可以通过索引器(list[i])访问,并且项目可以附加list.Add(item);。它们会自动生长。LinkedLists不能通过索引器访问,必须遍历。

你可以像这个一样声明方法

void merge(IEnumerator<int> left, IEnumerator<int> right, 
           List<int> full)
{
    // Jon Skeet's code goes here
}

你可以得到一个像这样的枚举器

IEnumerable<int> intEnumerable = ...;
IEnumerator<int> intEnumerator = intEnumerable.GetEnumerator();

IEnumerable<T>是由大多数通用集合类型实现的。非泛型集合通常实现IEnumerable

(针对@CodeInChaos的评论进行了编辑)。