Reputation: 2852
I have a factory function, and want to use it to create an Enumerable. In C# I didn't find a straight-forward declarative way to do the job. So I did it like this:
public IEnumerable<Contact> Get()
{
return Enumerable.Range(1, 5).Select(x => GenerateRandomContact());
}
Is there any better way?
// Expected format
public IEnumerable<Contact> Get()
{
return Enumerable.Generate(GenerateRandomContact).Take(5);
}
Upvotes: 6
Views: 5154
Reputation: 4119
public IEnumerable<Contact> Get()
{
for(int i = 1; i <= 5; i ++)
{
yield return GenerateRandomContact();
}
}
or if you want a specific amount you can do this
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();
}
}
You are expecting something like Enumerable.Generate()
In order to get a list of elements that you want to Enumerate, your Generate method should return an Enumerable Value. You are trying to create an extension method for Enumerable based on your approach. Before that try to see the implementation of 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;
}
In that case with the yield, .NET creates the IEnumerable class for you and based on a specific amount. Maybe you should consider passing the amount on the Generate method, or like the other extensions as Concat, you should pass the IEnumerable, see bellow how they pass to the Contact mehotd the IEnumerable first variable
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);
}
With that said.
You can use my first approach or your GenerateRandomContact should return an IEnumerable of Contact, but there is the problem, how much elements you want on that Random creation, or you don't specify the amount as bellow
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()}"
};
}
}
or you pass the parameter
But the whole problem now is, if you want to call your method as Enumerable.Generate, that is not possible, You can't have extension methods on static classes because extension methods are only applicable to instantiable types and static classes cannot be instantiated.
Your call should be
IEnumerable<Contact> contacts = new List<Contact>(){....}
//where contacts is an instance of type
contacts.Generate
even in the case you want this syntax
contacts.Generate(GenerateRandomContact).Take(5);
your extension method should accept a function but here I am relying on the previous GenerateRandomContact() where I put 100 random contacts
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>();
}
}
IMHO try to consider passing the amount that you want to take. Your syntax will change a little bit
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>();
}
}
}
Upvotes: 1
Reputation: 22038
Something like:
public static IEnumerable<T> Generate(Func<T> generator)
{
while(true)
yield return generator.Invoke();
}
But you can't extend static classes. So you should place it in a helper class.
Upvotes: 9