是否存在IEnumerable<;T>;在C#中
本文关键字:gt 存在 lt 是否 IEnumerable | 更新日期: 2023-09-27 18:19:35
或者,如果T的枚举器只是列出所有元素,那么使用vector是否安全?
C++中不需要它,原因如下:
C#只支持动态多态性。因此,要创建一个可重用的算法,您需要一个所有迭代器都将实现的接口。这就是IEnumerator<T>
,而IEnumerable<T>
是用于返回迭代器的工厂。
另一方面,C++模板支持duck类型。这意味着您不需要通过接口约束泛型类型参数来访问成员——编译器将根据模板的每个实例化的名称查找成员。
C++容器和迭代器具有隐式接口,这些接口等效于.NET IEnumerable<T>
、IEnumerator<T>
、ICollection<T>
、IList<T>
,即:
对于集装箱:
- CCD_ 7和CCD_
begin()
成员函数——满足对IEnumerable<T>::GetEnumerator()
的需求end()
成员函数--而不是IEnumerator<T>::MoveNext()
返回值
对于前向迭代器:
value_type
typedefoperator++
——而不是IEnumerator<T>::MoveNext()
operator*
和operator->
——而不是IEnumerator<T>::Current
- 来自
operator*
的引用返回类型--而不是IList<T>
索引器setter operator==
和operator!=
——在.NET中没有真正的等价物,但容器的end()
与IEnumerator<T>::MoveNext()
的返回值匹配
对于随机访问迭代器:
operator+
、operator-
、operator[]
——代替IList<T>
如果您定义了这些,那么标准算法将适用于您的容器和迭代器。不需要接口,也不需要虚拟功能。不使用虚拟函数会使C++泛型代码比等效的.NET代码更快,有时甚至更快。
注意:在编写泛型算法时,最好使用std::begin(container)
和std::end(container)
,而不是容器成员函数。这允许您的算法除了用于STL容器之外,还用于原始数组(没有成员函数)。原始数组和原始指针满足容器和迭代器的所有其他要求,只有一个例外。
据我所知,如果我们严格地坚持这个问题,答案是否定的。人们一直在回复C++中可用的替代品是什么,这可能是很好的信息,但不是答案,OP很可能已经知道了。
我完全不同意"不需要它"的说法,只是C++和.NET标准库的设计不同。IEnumerable<>的主要功能它是多态的,因此它使调用方能够使用他想要的任何类(数组、列表、集合等),同时仍然提供编译时强类型,即使在库API中也是故障安全的。
C++中唯一的替代方案是模板。但C++模板并不是安全类型的运行时泛型,它们基本上是一种宏。因此,首先使用C++中的模板,您必须向任何需要使用模板的人提供整个模板源代码。此外,如果您使库API模板化,您将失去保证对它的调用将编译的能力,并且代码不会自动进行自文档编制。
我完全同情其他同时使用C#和C++的程序员,并对这一点感到沮丧。
然而,C++2X计划添加包括范围在内的功能(可能满足OP?);以及概念(解决了模板的弱/坏类型检查问题——Bjarne Stroustrup自己承认的缺陷)和模块(可能有助于也可能没有助于减少纯头模板的痛苦)。
标准的C++方法是传递两个迭代器:
template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
for (; begin != end; ++begin)
{
do_something_with(*begin);
}
}
示例客户端代码:
std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(vec.begin(), vec.end());
std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(lst.begin(), lst.end());
int arr[] = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(arr + 0, arr + 8);
Yay通用编程!
IEnumerable<T>
在概念上与vector
非常不同。
IEnumerable
提供对对象序列的仅向前、只读访问,而不管哪个容器(如果有的话)容纳对象。vector
实际上是一个容器本身。
在C++中,如果您希望在不提供容器详细信息的情况下提供对容器的访问,则约定是传入两个迭代器,表示容器的开始和结束。
一个很好的例子是累积的C++STL定义,它可以与IEnumerable<T>。骨料
在C++中
int GetProduct(const vector<int>& v)
{
// We don't provide the container, but two iterators
return std::accumulate(v.begin(), v.end(), 1, multiplies<int>());
}
在C#中
int GetProduct(IEnumerable<int> v)
{
v.Aggregate(1, (l, r) => l*r);
}