代码之家  ›  专栏  ›  技术社区  ›  Matt

如何使for循环成为泛型?[副本]

  •  -2
  • Matt  · 技术社区  · 6 年前

    我在玩仿制药,并试图写一些类似的东西

    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循环?

    三。如何使for循环成为泛型?

    2 回复  |  直到 5 年前
        1
  •  4
  •   Matt    6 年前

    A 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,循环您喜欢的内容:

    static IEnumerable<double> MakeDoubles() => 
      MyFor(0.0, x => x <= 10.0, x => x + 1.0, x => x);
    
        2
  •  0
  •   Samuel Liew cicero lopes    6 年前

    下面的答案是对问题下先前评论的讨论的总结。多亏了 MistyK 他找到了解决问题的方法。


    问题是,目前在C中没有可以用一般方式使用的“数值”约束。

    因此,这样的for循环使用泛型类型 T 不起作用,因为编译器不允许: 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 ,第3个示例使用的增量为 0.75 .