我在玩泛型,并尝试编写类似的东西
IEnumerable<T> Numbers<T>(T a, T b)
where T: IEnumerable, IComparable
{
for (T i = a; i < b; i++)
{
yield return i;
}
}
实现一个数字生成器,该生成器可用于double,int,... 这将是一个通用的for循环。但是此示例未编译,它生成以下错误:
CS0019:运算符“ <”不能应用于类型“ T”和“ T”的操作数
CS0023:运算符'++'不能应用于类型'T'的操作数
原因似乎是数字没有任何约束,但是似乎没有任何实际的解决方案,here的某些答案正在对此进行解释。
注意:它的类似(非通用)版本可以编译:
IEnumerable<double> Numbers(double a, double b)
{
for (var i = a; i < b; i++)
{
yield return i;
}
}
IEnumerable<int> Numbers(int a, int b)
{
for (var i = a; i < b; i++)
{
yield return i;
}
}
如果两者都有,您可以像调用它一样
var intNumbers = Numbers((int)1, 10);
var doubleNumbers = Numbers((double)1, 10);
,并且由于参数的签名,选择了正确的版本。
本质上,问题是:
1。是否可以将通用函数编写为可以像
那样调用的函数var intNumbers = Numbers<int>(1, 10);
var doubleNumbers = Numbers<double>(1, 10);
如我的第一个示例所示?
(我不确定正确的约束,我认为where T: IEnumerable, IComparable
会成功,因为您需要比较i < b
,并且需要迭代到下一个更大的数字)。
2。 a)如何编写通用约束,使我可以递增和比较类型T
的变量?
2。 b)如果没有这样的约束,有没有办法用通用参数来模拟for循环?
3。如何使for循环通用?
答案 0 :(得分:4)
for
循环包含四个部分:
我们将break
和continue
排除在外,因为它们使事情复杂化了。
您希望将操作限制为产生值。精细。然后,我们想要的是Aggregate
的新版本,它产生一个值:
public static IEnumerable<R> MyFor<S, R>(
S initial,
Func<S, bool> test,
Func<S, S> increment,
Func<S, R> select)
{
for (S current = initial; test(current); current = increment(current))
yield return select(current);
}
我们完成了。现在,您只需提供必要的lambda,就可以进行任何for
循环:
static IEnumerable<double> MakeDoubles() =>
MyFor(0.0, x => x <= 10.0, x => x + 1.0, x => x);
答案 1 :(得分:0)
以下答案是对该问题下先前评论的讨论的总结。感谢MistyK找到解决问题的方法。
问题在于,C#中目前不存在可以以通用方式使用的“数字”约束。
因此,使用通用类型T
的此类for循环不起作用,因为编译器不允许:for (T i = a; i < b; i++)
作为一种解决方法,我们必须使用while
循环并将约束struct
与IComparable
一起使用。 yield
关键字没有任何限制,可以按预期运行。
根据实现方式(即是否要灵活递增),有两个函数可以解决此问题。
考虑以下代码:
using System;
using System.Collections.Generic;
public class Program
{
// Answer 1:
// The downside is that you need to provide your increment
// func and you can pass any type implementing
// IComparable.Usage: var xd = Numbers<int>(1, 5, x => { x++; return x; });
public static IEnumerable<T> Numbers<T>(T a, T b, Func<T, T> increment)
where T : struct, IComparable
{
var i = a; while (i.CompareTo(b) < 0)
{
yield return i; i = increment(i);
}
}
// Answer 2:
// Idea without passing increment function but can cause runtime error
// if increment is not implemented:
public static IEnumerable<T> Numbers<T>(T a, T b)
where T : struct, IComparable
{
dynamic i = a; while (i.CompareTo(b) < 0)
{
yield return i; i++;
}
}
public static void Main()
{
// implicit increment:
Numbers<double>(1, 5).Dump();
// explicit increment (+1), example 1:
Numbers<int>(1, 5, x => {x++; return x;}).Dump();
// explicit increment (+0.75), example 2:
Numbers<float>((float)1.5, (float)7.5, x => { x += (float)0.75; return x; }).Dump();
}
}
使用DotNetFiddle来执行
以上示例使用Numbers
,int
和double
调用float
,而第三个示例使用了0.75
的增量。