创建IEnumerable<>来自使用工厂功能的发电机方法

时间:2016-12-19 15:12:11

标签: c# linq

我有一个工厂函数,并希望用它来创建一个Enumerable。在C#中,我没有找到一种直接的声明式方法来完成这项工作。所以我这样做了:

public IEnumerable<Contact> Get()
{
    return Enumerable.Range(1, 5).Select(x => GenerateRandomContact());
}

还有更好的方法吗?

// Expected format
public IEnumerable<Contact> Get()
{
    return Enumerable.Generate(GenerateRandomContact).Take(5);
}

2 个答案:

答案 0 :(得分:6)

类似的东西:

public static IEnumerable<T> Generate(Func<T> generator)
{
    while(true)
        yield return generator.Invoke();
}

但是你不能扩展静态类。所以你应该将它放在一个帮助类中。

答案 1 :(得分:1)

public IEnumerable<Contact> Get()
{
    for(int i = 1; i <= 5; i ++)
    {
       yield return GenerateRandomContact();
    }
}

或者如果您想要特定金额,可以执行此操作

public IEnumerable<Contact> Get(int number)
{
    for(var i = 1; i <= number; i ++)  //is the same as for (var i = 1;; i++) you got the idea
    {
        yield return GenerateRandomContact();
    }
}

你期待像Enumerable.Generate()

这样的东西

为了获取要枚举的元素列表,Generate方法应该返回一个Enumerable Value。您正在尝试根据您的方法为Enumerable创建扩展方法。在此之前尝试查看Range

的实现
public static IEnumerable<int> Range(int start, int count) 
{
     long max = ((long)start) + count - 1;
     if (count < 0 || max > Int32.MaxValue) throw Error.ArgumentOutOfRange("count");
            return RangeIterator(start, count);
}

static IEnumerable<int> RangeIterator(int start, int count) 
{
    for (int i = 0; i < count; i++) yield return start + i;
}

在yield的情况下,.NET会根据特定的数量为您创建IEnumerable类。也许你应该考虑在Generate方法上传递数量,或者像Concat那样的其他扩展,你应该传递IEnumerable,请看下面它们如何传递给Contact mehotd IEnumerable第一个变量

public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 
{
    if (first == null) throw Error.ArgumentNull("first");
    if (second == null) throw Error.ArgumentNull("second");
    return ConcatIterator<TSource>(first, second);
}

说完了。

您可以使用我的第一种方法,或者您的GenerateRandomContact应该返回IEnumerable of Contact,但是存在问题,您想要在随机创建上需要多少元素,或者您没有将数量指定为以下

public IEnumerable<Contact> GenerateRandomContact()
 {

    var random = new Random(Environment.TickCount);
    for (int i = 0; i < 100; i++)
    {
        yield return new Contact
        {
            Name = $"Name_{random.Next()}"
        };
    }
 }

或您传递参数

但现在整个问题是,如果你想把你的方法称为Enumerable.Generate,这是不可能的,你不能在静态类上有扩展方法,因为扩展方法只适用于可实例化的类型和静态类无法实例化。

你的电话应该是

IEnumerable<Contact> contacts = new List<Contact>(){....}
//where contacts is an instance of type
contacts.Generate 

即使你想要这种语法

 contacts.Generate(GenerateRandomContact).Take(5);

你的扩展方法应该接受一个函数,但在这里我依赖于之前的GenerateRandomContact(),其中我放了100个随机联系人

public static class Extension
{
    public static IEnumerable<T> Generate<T>(this IEnumerable<T> elements, Func<IEnumerable<T>> func)
    {
        if (func != null)
        {
            return func();
        }

        return Enumerable.Empty<T>();
    }
}

恕我直言,请尝试考虑传递您想要的金额。你的语法会有所改变

class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<Contact> contacts = new List<Contact>()
            {
                new Contact
                {
                    Name = "Name1"
                },
                new Contact
                {
                    Name = "Name2"
                }
            }; //load your methods from the database or create them here

            var res = contacts.Generate(GenerateRandomContact, 5);

            Console.ReadLine();
        }

        static IEnumerable<Contact> GenerateRandomContact(int amount)
        {
            var random = new Random(Environment.TickCount);

            for (int i = 0; i < amount; i++)
            {
                yield return new Contact
                {
                    Name = $"Name_{random.Next()}"
                };
            }
        }

    }

    public class Contact
    {
        public string Name { get; set; }
    }

    public static class Extension
    {
        public static IEnumerable<T> Generate<T>(this IEnumerable<T> elements, Func<int, IEnumerable<T>> func, int amount)
        {
            if (func != null)
            {
                return func(amount);
            }

            return Enumerable.Empty<T>();
        }
    }
}